package com.to8to.scene.server;

import static com.mongodb.QueryOperators.GT;
import static com.mongodb.QueryOperators.GTE;
import static com.mongodb.QueryOperators.IN;
import static com.mongodb.QueryOperators.LTE;
import static com.mongodb.QueryOperators.NE;
import static com.mongodb.QueryOperators.NIN;
import static com.to8to.scene.server.Commons.mongo;
import static com.to8to.scene.server.Tables.Comment;
import static com.to8to.scene.server.Tables.Diary;
import static com.to8to.scene.server.Tables.Product;
import static com.to8to.scene.server.Validator.validBehaveReq;
import static com.to8to.scene.server.Validator.validCommentReq;
import static com.to8to.scene.server.Validator.validDiaryReq;
import static com.to8to.scene.server.Validator.validListBehaviorRequest;
import static com.to8to.scene.server.Validator.validProductReq;
import static com.to8to.scene.server.Validator.validRecommendSceneRequest;
import static com.to8to.scene.server.Validator.validSceneReq;
import static com.to8to.scene.server.Validator.validVerifyRequest;
import static com.to8to.thrift.diary.consts.constConstants.CONF_PROG_TYPE_1;
import static com.to8to.thrift.diary.consts.constConstants.CONF_PROG_TYPE_2;
import static com.to8to.thrift.diary.consts.constConstants.ENTITY_STATUS_DEL;
import static com.to8to.thrift.diary.consts.constConstants.ENTITY_STATUS_DRAFT;
import static com.to8to.thrift.diary.consts.constConstants.ENTITY_STATUS_NORM;
import static com.to8to.thrift.diary.consts.constConstants.ENTITY_STATUS_PENDING;
import static com.to8to.thrift.diary.consts.constConstants.ENTITY_STATUS_REJECT;
import static com.to8to.thrift.diary.consts.constConstants.RECENT_EVENT_SCENE;
import static com.to8to.thrift.diary.consts.constConstants.SCENE_OWNER_EPT;
import static com.to8to.thrift.diary.consts.constConstants.SCENE_OWNER_NPJ;
import static com.to8to.thrift.diary.consts.constConstants.SCENE_OWNER_PJ;
import static com.to8to.thrift.diary.consts.constConstants.TARGET_TYPE_COMMENT;
import static com.to8to.thrift.diary.consts.constConstants.TARGET_TYPE_DIARY;
import static com.to8to.thrift.diary.consts.constConstants.TARGET_TYPE_SCENE;
import static com.to8to.thrift.diary.consts.constConstants.VERSION_2_5;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.thrift.TBase;
import org.apache.thrift.TException;
import org.elasticsearch.common.collect.Lists;
import org.jboss.netty.util.internal.ConcurrentHashMap;
import org.jsoup.Jsoup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mongodb.AggregationOutput;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.QueryOperators;
import com.mongodb.WriteResult;
import com.mongodb.util.JSON;
import com.to8to.commons.utils.StringUtil;
import com.to8to.commons.utils.ThriftUtil;
import com.to8to.scene.deamon.StatWriter;
import com.to8to.scene.util.LogUtil;
import com.to8to.thrift.diary.advanced.AddDiaryProductRequest;
import com.to8to.thrift.diary.advanced.JudgeProductUsedRequest;
import com.to8to.thrift.diary.advanced.JudgeProductUsedResponse;
import com.to8to.thrift.diary.advanced.ListAllCategoryStatRequest;
import com.to8to.thrift.diary.advanced.ListAllCategoryStatResponse;
import com.to8to.thrift.diary.advanced.ListCompanyAllScenesRequest;
import com.to8to.thrift.diary.advanced.ListCompanyAllScenesResponse;
import com.to8to.thrift.diary.advanced.ListCompanyTopDiaryRequest;
import com.to8to.thrift.diary.advanced.ListCompanyTopDiaryResponse;
import com.to8to.thrift.diary.advanced.ListJingXuanSceneRequest;
import com.to8to.thrift.diary.advanced.ListJingXuanSceneResponse;
import com.to8to.thrift.diary.advanced.ListNewestSceneRequest;
import com.to8to.thrift.diary.advanced.ListNewestSceneResponse;
import com.to8to.thrift.diary.advanced.ListProgressImagesRequest;
import com.to8to.thrift.diary.advanced.ListProgressImagesResponse;
import com.to8to.thrift.diary.advanced.ListSceneCondition;
import com.to8to.thrift.diary.advanced.ListSceneDetailRequest;
import com.to8to.thrift.diary.advanced.ListSceneDetailResponse;
import com.to8to.thrift.diary.advanced.ListSceneDiaryViewsRequest;
import com.to8to.thrift.diary.advanced.ListSceneDiaryViewsResponse;
import com.to8to.thrift.diary.advanced.ListStatisticRequest;
import com.to8to.thrift.diary.advanced.ListStatisticResponse;
import com.to8to.thrift.diary.advanced.RecommendSceneRequest;
import com.to8to.thrift.diary.advanced.RecommendSceneResponse;
import com.to8to.thrift.diary.advanced.RemoveDiaryProductRequest;
import com.to8to.thrift.diary.advanced.VerifyRequest;
import com.to8to.thrift.diary.advanced.VerifyResponse;
import com.to8to.thrift.diary.behavior.BehaveRequest;
import com.to8to.thrift.diary.behavior.BehaveResponse;
import com.to8to.thrift.diary.behavior.ListBehaviorRequest;
import com.to8to.thrift.diary.behavior.ListBehaviorResponse;
import com.to8to.thrift.diary.comment.CreateCommentRequest;
import com.to8to.thrift.diary.comment.DeleteCommentRequest;
import com.to8to.thrift.diary.comment.ListCommentsRequest;
import com.to8to.thrift.diary.comment.ListCommentsResponse;
import com.to8to.thrift.diary.consts.constConstants;
import com.to8to.thrift.diary.diary.CreateDiaryRequest;
import com.to8to.thrift.diary.diary.DeleteDiaryRequest;
import com.to8to.thrift.diary.diary.ListDiariesRequest;
import com.to8to.thrift.diary.diary.ListDiariesResponse;
import com.to8to.thrift.diary.diary.UpdateDiaryRequest;
import com.to8to.thrift.diary.message.ListMessagesRequest;
import com.to8to.thrift.diary.message.ListMessagesResponse;
import com.to8to.thrift.diary.message.UpdateMessageRequest;
import com.to8to.thrift.diary.message.UpdateMessageResponse;
import com.to8to.thrift.diary.module.CreateResponse;
import com.to8to.thrift.diary.module.DeleteResponse;
import com.to8to.thrift.diary.module.TBehavior;
import com.to8to.thrift.diary.module.TComment;
import com.to8to.thrift.diary.module.TCommentView;
import com.to8to.thrift.diary.module.TDiary;
import com.to8to.thrift.diary.module.TDiaryInfoView;
import com.to8to.thrift.diary.module.TDiaryStat;
import com.to8to.thrift.diary.module.TImage;
import com.to8to.thrift.diary.module.TMessage;
import com.to8to.thrift.diary.module.TProduct;
import com.to8to.thrift.diary.module.TProgressDiaryCount;
import com.to8to.thrift.diary.module.TRange;
import com.to8to.thrift.diary.module.TScene;
import com.to8to.thrift.diary.module.TSceneDiaryView;
import com.to8to.thrift.diary.module.TSceneLundan;
import com.to8to.thrift.diary.module.TSceneStat;
import com.to8to.thrift.diary.module.TSortField;
import com.to8to.thrift.diary.module.TStElem;
import com.to8to.thrift.diary.module.TStOrderList;
import com.to8to.thrift.diary.module.TStType;
import com.to8to.thrift.diary.module.TTagDiaryCount;
import com.to8to.thrift.diary.module.TUserScore;
import com.to8to.thrift.diary.module.UpdateResponse;
import com.to8to.thrift.diary.product.CreateProductRequest;
import com.to8to.thrift.diary.product.DeleteProductRequest;
import com.to8to.thrift.diary.product.ListProductRequest;
import com.to8to.thrift.diary.product.ListProductResponse;
import com.to8to.thrift.diary.product.UpdateProductRequest;
import com.to8to.thrift.diary.scene.CreateSceneRequest;
import com.to8to.thrift.diary.scene.DeleteSceneRequest;
import com.to8to.thrift.diary.scene.ListCommunityRequest;
import com.to8to.thrift.diary.scene.ListCommunityResponse;
import com.to8to.thrift.diary.scene.ListCompaniesRequest;
import com.to8to.thrift.diary.scene.ListCompaniesResponse;
import com.to8to.thrift.diary.scene.ListSceneRawRequest;
import com.to8to.thrift.diary.scene.ListScenesRequest;
import com.to8to.thrift.diary.scene.ListScenesResponse;
import com.to8to.thrift.diary.scene.UpdateSceneRawRequest;
import com.to8to.thrift.diary.scene.UpdateSceneRequest;
import com.to8to.thrift.diary.service.SceneDiaryService;
import com.to8to.thrift.diary.stconfigs.GetAllStConfigIdMapRequest;
import com.to8to.thrift.diary.stconfigs.GetAllStConfigIdMapResponse;
import com.to8to.thrift.diary.stconfigs.GetStConfigSubIdMapRequest;
import com.to8to.thrift.diary.stconfigs.GetStConfigSubIdMapResponse;
import com.to8to.thrift.diary.stconfigs.GetStOrderListRequest;
import com.to8to.thrift.diary.stconfigs.GetStOrderListResponse;
import com.to8to.thrift.diary.user.ListUsersRequest;
import com.to8to.thrift.diary.user.ListUsersResponse;
import com.to8to.thrift.diary.user.UpdateUserRequest;

public class SceneDiaryIface implements SceneDiaryService.Iface
{
    public static Logger logger = LoggerFactory.getLogger(SceneDiaryIface.class);

    public static String[] tables = { "", Tables.Scene, Tables.Diary, Tables.Product, Tables.Comment };

    private static ArrayList<Integer> oldTagsProdSele = Lists.newArrayList(Arrays.asList(4, 8, 11, 16, 20, 32, 38));

    private static ArrayList<Integer> oldTagsSsn = Lists.newArrayList(Arrays.asList(501, 502, 503, 504, 505, 506, 507, 508));

    int MAX_LIMIT = 100;

    int TOP_USERS = 10;

    /**
     * 配置的缓存Map
     */
    public static Map<String, List<TStType>> configMap_v20 = new ConcurrentHashMap<String, List<TStType>>();

    public static Map<String, List<TStType>> configMap_v25 = new ConcurrentHashMap<String, List<TStType>>();

    private void traceThrift(TBase<?, ?> base)
    {
        logger.debug(base.getClass().getName() + ":" + ThriftUtil.thriftObject2DBObject(base).toString());
    }

    /**
     * 将thrift对象转换成{@link DBObject}对象   
     * null值忽略
     */
    public static DBObject thriftObject2DBObject(TBase<?, ?> thriftObject)
    {
        return ThriftUtil.thriftObject2DBObject(thriftObject, ThriftUtil.NullBehavior.IGNORE);
    }

    /**
     * 获取要更新的BasicDBObject
     * 
     * @param fields
     *            指定更新字段
     * @param base
     *            传入原型全内容
     * @return 更新字段
     */
    public static BasicDBObject getUpdateDBObj(List<String> fields, TBase<?, ?> base)
    {
        if (null == base)
            return null;
        BasicDBObject obj_db = (BasicDBObject) thriftObject2DBObject(base);
        BasicDBObject update_obj_db = new BasicDBObject();
        for (String update_field : fields)
        {
            update_obj_db.put(update_field, obj_db.get(update_field));
        }
        return update_obj_db;
    }

    /**
     * 查询现场列表
     */
    @Override
    public ListScenesResponse ListScenes(ListScenesRequest req) throws TException
    {
        List<String> fields = req.getFields();
        ListScenesResponse res = new ListScenesResponse();
        res.setTrace_tag(req.getTrace_tag());

        if (null == fields || fields.isEmpty())
        {
            res.setRes("Fields is null! needed fields,please refet to module.thrift!");
            return res;
        }

        BasicDBObject query = new BasicDBObject();

        if (fields.contains("_ALL"))//查询全部
        {
        }
        else
        {
            if (req.getSortSize() == 0)
                req.setSort(Arrays.asList(new TSortField("ctime", -1)));

            if (req.getStatusSize() == 0)
                req.setStatus(Arrays.asList(ENTITY_STATUS_NORM));

            if (fields.contains(ListScenesRequest._Fields._IDS.getFieldName()) && req.get_idsSize() > 0)
                query.put(TScene._Fields._ID.getFieldName(), new BasicDBObject(IN, req.get_ids()));

            if (fields.contains(ListScenesRequest._Fields.STYLE.getFieldName()) && req.getStyleSize() > 0)
            {
                List<DBObject> or = new ArrayList<DBObject>();
                for (Integer style : req.getStyle())
                    or.add(new BasicDBObject(ListScenesRequest._Fields.STYLE.getFieldName(), style));
                query.put("$or", or);
            }

            if (fields.contains(ListScenesRequest._Fields.AREA_RANGE.getFieldName()))
            {
                TRange area_range = req.getArea_range();
                query.put(TScene._Fields.AREA.getFieldName(), new BasicDBObject(GTE, area_range.getMini_value()).append(LTE, area_range.getMax_value()));
            }

            if (fields.contains(ListScenesRequest._Fields.BUDGET_RANGE.getFieldName()))
            {
                TRange budget_range = req.getBudget_range();
                query.put(TScene._Fields.BUDGET.getFieldName(), new BasicDBObject(GTE, budget_range.getMini_value()).append(LTE, budget_range.getMax_value()));
            }

            if (fields.contains(ListScenesRequest._Fields.FTIME.getFieldName()))
            {
                if (req.getFtime() > 0)
                    query.put(TScene._Fields.FTIME.getFieldName(), new BasicDBObject(GT, 0));
            }

            if (fields.contains(ListScenesRequest._Fields.OP_TOP.getFieldName()) && req.getOp_top() > 0)
                query.put(TScene._Fields.OP_TAGS.getFieldName(), new BasicDBObject(NE, null));

            if (fields.contains(ListScenesRequest._Fields.FILTER_RESULT.getFieldName()) && req.getFilter_resultSize() > 0)
                query.put(TScene._Fields.FILTER_RESULT.getFieldName(), new BasicDBObject(IN, req.getFilter_result()));

            if (fields.contains(ListScenesRequest._Fields.EXCLUDE_IDS.getFieldName()) && req.getExclude_idsSize() > 0)
                query.put(TScene._Fields._ID.getFieldName(), new BasicDBObject(NIN, req.getExclude_ids()));

            if (fields.contains(ListScenesRequest._Fields.COM_IDS.getFieldName()) && req.getCom_idsSize() > 0)
                query.put(TScene._Fields.COM_ID.getFieldName(), new BasicDBObject(IN, req.getCom_ids()));

            if (fields.contains(ListScenesRequest._Fields.OWNER_IDS.getFieldName()) && req.getOwner_idsSize() > 0)
                query.put(TScene._Fields.OWNER_ID.getFieldName(), new BasicDBObject(IN, req.getOwner_ids()));

            if (fields.contains(ListScenesRequest._Fields.COMM_NAME.getFieldName()))
            {
                if (!StringUtil.isEmpty(req.getComm_name()))
                {
                    Pattern pattern = Pattern.compile("^.*" + req.getComm_name() + ".*$", Pattern.CASE_INSENSITIVE);
                    query.put(TScene._Fields.COMM_NAME.getFieldName(), pattern);
                }
            }

            if (fields.contains(ListScenesRequest._Fields.GONGDIJDS.getFieldName()) && req.getGongdijdsSize() > 0)
                query.put(TScene._Fields.GONGDIJD.getFieldName(), new BasicDBObject(IN, req.getGongdijds()));

            if (fields.contains(ListScenesRequest._Fields.HAS_PROJECT_ID.getFieldName()))
            {
                List<String> null_list = new ArrayList<String>();
                null_list.add("");
                null_list.add(null);
                if (req.getHas_project_id() > 0)
                    query.put(TScene._Fields.PROJECT_ID.getFieldName(), new BasicDBObject(NIN, null_list));
                else if (req.getHas_project_id() == 0)
                    query.put(TScene._Fields.PROJECT_ID.getFieldName(), new BasicDBObject(IN, null_list));
            }

            if (fields.contains(ListScenesRequest._Fields.PROJECT_IDS.getFieldName()) && req.getProject_idsSize() > 0)
                query.put(TScene._Fields.PROJECT_ID.getFieldName(), new BasicDBObject(IN, req.getProject_ids()));

            if (fields.contains(ListScenesRequest._Fields.NAME_C.getFieldName()))
            {
                if (!StringUtil.isEmpty(req.getName_c()))
                {
                    Pattern pattern = Pattern.compile("^.*" + req.getName_c() + ".*$", Pattern.CASE_INSENSITIVE);
                    query.put(TScene._Fields.NAME_C.getFieldName(), pattern);
                }
            }

            if (fields.contains(ListScenesRequest._Fields.NAME_U.getFieldName()))
            {
                if (!StringUtil.isEmpty(req.getName_u()))
                {
                    Pattern pattern = Pattern.compile("^.*" + req.getName_u() + ".*$", Pattern.CASE_INSENSITIVE);
                    query.put(TScene._Fields.NAME_U.getFieldName(), pattern);
                }
            }

            if (fields.contains(ListScenesRequest._Fields.COM_NAME.getFieldName()))
            {
                if (!StringUtil.isEmpty(req.getCom_name()))
                {
                    Pattern pattern = Pattern.compile("^.*" + req.getCom_name() + ".*$", Pattern.CASE_INSENSITIVE);
                    query.put(TScene._Fields.COM_NAME.getFieldName(), pattern);
                }
            }

            if (fields.contains(ListScenesRequest._Fields.SHOW_INDEX.getFieldName()))
            {
                if (req.getShow_index() > 0)
                    query.put(TScene._Fields.SHOW_INDEX.getFieldName(), new BasicDBObject(GT, 0));
                else if (req.getShow_index() == 0)
                    query.put(TScene._Fields.SHOW_INDEX.getFieldName(), 0);
            }

            if (fields.contains(ListScenesRequest._Fields.TYPE_IDS.getFieldName()) && req.getType_idsSize() > 0)
                query.put(TScene._Fields.TYPE_ID.getFieldName(), new BasicDBObject(IN, req.getType_ids()));

            if (fields.contains(ListScenesRequest._Fields.CGATE.getFieldName()))
                query.put(TScene._Fields.CGATE.getFieldName(), req.getCgate());

            if (fields.contains(ListScenesRequest._Fields.CITY_IDS.getFieldName()) && req.getCity_idsSize() > 0)
                query.put(TScene._Fields.CITY_ID.getFieldName(), new BasicDBObject(IN, req.getCity_ids()));

            if (fields.contains(ListScenesRequest._Fields.TOWN_IDS.getFieldName()) && req.getTown_idsSize() > 0)
                query.put(TScene._Fields.TOWN_ID.getFieldName(), new BasicDBObject(IN, req.getTown_ids()));

            if (fields.contains(ListScenesRequest._Fields.CTIME_RANGE.getFieldName()))
            {
                TRange ctime_range = req.getCtime_range();
                query.put(TScene._Fields.CTIME.getFieldName(), new BasicDBObject(GTE, ctime_range.getMini_value()).append(LTE, ctime_range.getMax_value()));
            }

            if (fields.contains(ListScenesRequest._Fields.USED.getFieldName()))
                query.put(TScene._Fields.USED.getFieldName(), req.isUsed());

            if (fields.contains(ListScenesRequest._Fields.OWNER_TYPE.getFieldName()))
                query.put(TScene._Fields.STAT.getFieldName() + "." + TSceneStat._Fields.OWNER_TYPE.getFieldName(), req.getOwner_type());

            if (fields.contains(ListScenesRequest._Fields.PROGRESS_IDS.getFieldName()) && req.getProgress_idsSize() > 0)
                query.put(TScene._Fields.PROGRESS_ID.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getProgress_ids()));

            if (fields.contains(ListScenesRequest._Fields.CASE_IDS.getFieldName()) && req.getCase_idsSize() > 0)
                query.put(TScene._Fields.CASE_ID.getFieldName(), new BasicDBObject(IN, req.getCase_ids()));

            query.put(TScene._Fields.STATUS.getFieldName(), new BasicDBObject(IN, req.getStatus()));

            if (fields.contains(ListScenesRequest._Fields.HAS_COVER_IMG.getFieldName()) && req.getHasCoverImg() == 1)
                query.put(TScene._Fields.COVER_IMAGE.getFieldName(), new BasicDBObject(NE, null));

            if (fields.contains(ListScenesRequest._Fields.HAS_COVER_IMG_C.getFieldName()) && req.getHasCoverImg_c() == 1)
                query.put(TScene._Fields.COVER_IMAGE_C.getFieldName(), new BasicDBObject(NE, null));

            if (fields.contains(ListScenesRequest._Fields.SHOW_EMPTY_SCENE.getFieldName()))
            {
                if (req.getShowEmptyScene() == 0)//去掉没有日记的现场
                    query.put("stat.owner_type", new BasicDBObject(IN, Arrays.asList(SCENE_OWNER_NPJ, SCENE_OWNER_PJ)));
            }

            if (fields.contains(ListScenesRequest._Fields.PUBLISH_SOURCES.getFieldName()))
            {
                if (req.getPublish_sourcesSize() > 0)
                    query.put(TScene._Fields.PUBLISH_SOURCE.getFieldName(), new BasicDBObject(IN, req.getPublish_sources()));
            }

            if (fields.contains(ListScenesRequest._Fields.COMM_IDS.getFieldName()) && req.getComm_idsSize() > 0)
                query.put(TScene._Fields.COMM_ID.getFieldName(), new BasicDBObject(IN, req.getComm_ids()));

            if (fields.contains(ListScenesRequest._Fields.HOUSE_TYPES.getFieldName()) && req.getHouse_typesSize() > 0)
                query.put(TScene._Fields.HOUSE_TYPE.getFieldName(), new BasicDBObject(IN, req.getHouse_types()));

            if (fields.contains(ListScenesRequest._Fields.PHONES.getFieldName()))
            {
                if (req.getPhonesSize() > 0)
                    query.put(TScene._Fields.PHONE.getFieldName(), new BasicDBObject(IN, req.getPhones()));
            }

            if (fields.contains(ListScenesRequest._Fields.EQUAL_SEARCH_FIELDS.getFieldName()) && req.getEqual_search_fieldsSize() > 0)
            {
                Set<ListScenesRequest._Fields> scene_key_set = ListScenesRequest.metaDataMap.keySet();
                for (ListScenesRequest._Fields key : scene_key_set)
                {
                    for (String equal_field : req.getEqual_search_fields())
                    {
                        if (key.getFieldName().equals(equal_field))
                        {
                            query.put(equal_field, req.getFieldValue(key));
                        }
                    }
                }
            }
        }

        int offset = req.getOffset();
        int limit = req.getLimit();

        BasicDBObject sort = new BasicDBObject();
        List<TSortField> sort_fields = req.getSort();
        if (null != sort_fields)
        {
            for (TSortField sf : sort_fields)
                sort.put(sf.getField(), sf.getAsc());
        }

        logger.trace("search scene table,trace_tag: {}, query: {}, sort:{}, offset:{}, limit:{}", req.getTrace_tag(), query, sort, offset, limit);

        BasicDBObject resultFields = null;
        if (req.getResultFieldsSize() > 0)
        {
            resultFields = new BasicDBObject();
            for (String str : req.getResultFields())
                resultFields.append(str, 1);
        }

        DBCursor c = null;

        if (query.containsField(TScene._Fields.COMM_ID.getFieldName()))
            c =
                Commons.mongo.cursor(Tables.Scene, query, resultFields, sort, offset, limit).hint(new BasicDBObject(TScene._Fields.COMM_ID.getFieldName(), 1));
        else if (query.containsField(TScene._Fields._ID.getFieldName()))
            c =
                Commons.mongo.cursor(Tables.Scene, query, resultFields, sort, offset, limit).hint(new BasicDBObject(TScene._Fields._ID.getFieldName(), 1));
        else if (query.containsField(TScene._Fields.CITY_ID.getFieldName()))
            c =
                Commons.mongo.cursor(Tables.Scene, query, resultFields, sort, offset, limit).hint(new BasicDBObject(TScene._Fields.CITY_ID.getFieldName(), 1));
        else if (query.containsField(TScene._Fields.COM_ID.getFieldName()))
            c =
                Commons.mongo.cursor(Tables.Scene, query, resultFields, sort, offset, limit).hint(new BasicDBObject(TScene._Fields.COM_ID.getFieldName(), 1));
        else if (query.containsField(TScene._Fields.OWNER_ID.getFieldName()))
            c =
                Commons.mongo.cursor(Tables.Scene, query, resultFields, sort, offset, limit).hint(new BasicDBObject(TScene._Fields.OWNER_ID.getFieldName(), 1));
        else if (query.containsField(TScene._Fields.CASE_ID.getFieldName()))
            c =
                Commons.mongo.cursor(Tables.Scene, query, resultFields, sort, offset, limit).hint(new BasicDBObject(TScene._Fields.CASE_ID.getFieldName(), 1));
        else
            c = Commons.mongo.cursor(Tables.Scene, query, resultFields, sort, offset, limit);

        DBObject cur_explain = c.explain();
        Object time_spend = cur_explain.get("millis");
        logger.trace("trace_tag:{},time_spend:{}", req.getTrace_tag(), time_spend);
        if (null != time_spend && StringUtil.isNumeric(time_spend.toString()))
        {
            long time_long = Long.parseLong(time_spend.toString());
            if (time_long > 100)
                logger.warn("trace_tag:{} 超过100ms explain:{}", req.getTrace_tag(), cur_explain);
        }

        int total = c.count();
        res.setTotal(total);
        res.setOffset(offset);
        res.setLimit(limit);
        res.setResults(new ArrayList<TScene>());

        for (DBObject o : c)
        {
            TScene scene = ThriftUtil.dbObject2ThriftObject(o, TScene.class);
            res.addToResults(scene);
        }

        //add related diary
        if (req.getFields().contains(ListScenesRequest._Fields.WITH_DIARY.getFieldName()) && req.getWith_diary() > 0)
        {
            int diary_limit = req.getDiary_limit();
            List<TSortField> diary_sort_fields = req.getDiary_sort();
            for (TScene scene : res.getResults())
            {
                logger.trace("find diaries for scene:{}", scene.get_id());
                ListDiariesRequest diary_req = new ListDiariesRequest();
                diary_req.setTrace_tag(req.getTrace_tag());
                diary_req.setVersion(constConstants.VERSION_2_5);
                List<String> diary_fields = new ArrayList<String>();
                diary_req.setFields(diary_fields);
                diary_fields.add(ListDiariesRequest._Fields.SCENE_IDS.getFieldName());

                diary_req.setScene_ids(Arrays.asList(new String[] { scene.get_id() }));

                if (req.getDiary_statusSize() > 0)
                {
                    diary_fields.add(ListDiariesRequest._Fields.STATUS.getFieldName());
                    diary_req.setStatus(req.getDiary_status());
                }

                if (req.getDiary_publish_sourcesSize() > 0)
                {
                    diary_fields.add(ListDiariesRequest._Fields.PUBLISH_SOURCES.getFieldName());
                    diary_req.setPublish_sources(req.getDiary_publish_sources());
                }

                if (req.getDiary_platformSize() > 0)
                {
                    diary_fields.add(ListDiariesRequest._Fields.PLATFORMS.getFieldName());
                    diary_req.setPlatforms(req.getDiary_platform());
                }

                diary_req.setLimit(diary_limit);
                diary_req.setSort(diary_sort_fields);
                scene.setDiaries(this.ListDiaries(diary_req).getResults());

            }
        }

        return res;
    }

    private void setUrlType(TDiary diary)
    {
        if (diary == null)
            return;

        int content_len = 0;

        if (diary.getContent() != null)
        {
            String content = Jsoup.parse(diary.getContent()).text();
            content = StringUtil.trim(content);
            content_len = content.length();
        }

        int image_num = diary.getImagesSize();

        String urlType = null;

        if (content_len >= 100 || image_num >= 5)
            urlType = "y";
        else
            urlType = "n";

        diary.setUrl_type(urlType);
    }

    /**
     * 获取当前时间戳，单位秒。 
     */
    public static int getCurrentTimeStamp()
    {
        return (int) (System.currentTimeMillis() / 1000);
    }

    public static int getSecTime(long millis)
    {
        return (int) (millis / 1000);
    }

    /**
     * 保存thrift对象
     * 【注】： 1:使用了自增id 2:会设置创建时间，更新时间
     * @param tableNam 表名
     * @param t 需要保存的thrift对象
     * @param _id 数据id，如果为Null，则生成一个id
     * @return 更新结果
     */
    public <T extends TBase<?, ?>> UpdateResult save(String tableName, T t, String _id, String trace_tag)
    {
        if (t == null)
        {
            return null;
        }

        DBObject dbObject = thriftObject2DBObject(t);

        if (StringUtil.isEmpty(_id))
        {
            // 获取自增id
            long num = mongo.getAutoincNumber(tableName, true);
            _id = num + "";
        }

        dbObject.put("_id", _id);

        logger.debug("insert into table : {}, value : {}, trace : {}", tableName, dbObject, trace_tag);

        WriteResult wr = mongo.save(tableName, dbObject);

        UpdateResult updateResult = new UpdateResult();
        updateResult.setWriteResult(wr);

        updateResult.setId(_id);

        return updateResult;
    }

    /**
     * 创建对象
     * @param req thrift对象
     */
    public <T extends TBase<?, ?>> CreateResponse create(String tableName, T t, String trace_tag) throws TException
    {
        return create(tableName, t, null, trace_tag);
    }

    /**
     * 创建对象
     * @param req thrift对象
     */
    public <T extends TBase<?, ?>> CreateResponse create(String tableName, T t, String _id, String trace_tag) throws TException
    {

        CreateResponse res = new CreateResponse();
        res.setTrace_tag(trace_tag);

        UpdateResult updateResult = save(tableName, t, _id, trace_tag);

        if (updateResult.getWriteResult() == null)
            res.setRes("db fail!");
        res.set_id(updateResult.getId());
        return res;
    }

    /**
     * 批量删除（伪删，置status为负1）
     * 
     * @param tableName
     *            表名
     * @param ids
     *            需要删除的id
     * @return 更新结果
     */
    public UpdateResult fakeDeleteBatch(String tableName, List<String> ids, String trace_tag)
    {

        UpdateResult updateResult = new UpdateResult();

        if (ids == null || ids.size() == 0)
        {
            logger.warn("ids is unset!");
            return updateResult;
        }
        DBObject cond = new BasicDBObject("_id", new BasicDBObject(IN, ids));
        // 删除操作就是置status为-1
        DBObject oper = new BasicDBObject("$set", new BasicDBObject("status", ENTITY_STATUS_DEL).append("utime", getCurrentTimeStamp()));
        logger.debug("fake delete ids : {} from: {} trace_tag: {}", ids, tableName, trace_tag);
        WriteResult wr = mongo.update(tableName, cond, oper, false, true);
        updateResult.setWriteResult(wr);
        return updateResult;
    }

    /**
     * 伪删除
     * @param ids 所有要删除的id
     */
    public <T extends TBase<?, ?>> DeleteResponse fakeDelete(String tableName, List<String> ids, String trace_tag) throws TException
    {

        DeleteResponse res = new DeleteResponse();
        res.setTrace_tag(trace_tag);

        // 批量伪删除
        UpdateResult updateResult = fakeDeleteBatch(tableName, ids, trace_tag);

        if (updateResult.getWriteResult() != null)
            res.setNum(updateResult.getWriteResult().getN());

        return res;
    }

    @Override
    public CreateResponse CreateScene(CreateSceneRequest req) throws TException
    {
        String validResult = validSceneReq(req);
        if (validResult != null)
            return new CreateResponse(validResult, null, req.getTrace_tag());

        TSceneStat stat = new TSceneStat();
        stat.setRecent_time(getCurrentTimeStamp());
        stat.setRecent_user(req.getScene().getOwner_id());
        stat.setRecent_event(RECENT_EVENT_SCENE);
        stat.setOwner_type(SCENE_OWNER_EPT);

        // 获取自增id
        long num = mongo.getAutoincNumber(Tables.Scene, true);
        String _id = num + "";
        stat.setScene_id(_id);

        // 置初始状态
        TScene scene = req.getScene();
        scene.setStat(stat);
        scene.setStatus(constConstants.ENTITY_STATUS_PENDING);

        // 设置现场名字
        // 强哥的业主工地才不设置
        if (!(constConstants.PUBLISH_SCENE_SOURCE_OWNER == scene.getPublish_source() && (constConstants.PLATFORM_ANDROID == scene.getPlatform() || constConstants.PLATFORM_IOS == scene.getPlatform())))
        {
            String name_c = scene.getName_c();
            String name_u = scene.getName_u();
            if (StringUtil.isEmpty(name_c) && StringUtil.isEmpty(name_u))
            {
                String scene_name = scene.getCom_name() + scene.getOwner_name();
                if (!StringUtil.isEmpty(scene_name))
                    scene_name += "的";
                scene_name += "装修日记";
                scene.setName_c(scene_name);
                scene.setName_u(scene_name);
            }
            else if (StringUtil.isEmpty(name_c) && !StringUtil.isEmpty(name_u))
            {
                scene.setName_c(name_u);
            }
            else if (!StringUtil.isEmpty(name_c) && StringUtil.isEmpty(name_u))
            {
                scene.setName_u(name_c);
            }
        }

        // 设置创建时间
        scene.setCtime(getCurrentTimeStamp());
        scene.setUtime(scene.getCtime());

        CreateResponse resp = create(Tables.Scene, scene, _id, req.getTrace_tag());

        return resp;
    }

    @Override
    public UpdateResponse UpdateScene(UpdateSceneRequest req) throws TException
    {

        UpdateResponse res = new UpdateResponse();
        res.setTrace_tag(req.getTrace_tag());

        TScene update_scene = req.getScene();

        List<String> fields = req.getFields();

        //  1.检验未填内容
        if (StringUtil.isEmpty(req.get_id()) || null == fields || fields.isEmpty())
        {
            res.setRes("needed fields,please refet to module.thrift!");
            return res;
        }
        //  2.过滤非法内容
        if (fields.contains(TScene._Fields._ID.getFieldName())
            || fields.contains(TScene._Fields.CTIME.getFieldName())
            || fields.contains(TScene._Fields.UTIME.getFieldName())
            || fields.contains(TScene._Fields.STAT.getFieldName())
            || fields.contains(TScene._Fields.STATUS.getFieldName()))
        {
            res.setRes("illegel fields,please refet to module.thrift!");
            return res;
        }

        //   3.过滤非法用户
        if (req.getNeed_validate() > 0)
        {
            DBObject origin_scene_db = mongo.findById(Tables.Scene, req.get_id());

            if (null != origin_scene_db)
            {
                TScene origin_scene = ThriftUtil.dbObject2ThriftObject(origin_scene_db, TScene.class);
                if (!StringUtil.equals(req.getUser_id(), origin_scene.getOwner_id()))
                {
                    res.setRes("unvalidated user!");
                    return res;
                }
            }
        }

        // 用户操作才更新更新时间
        if (req.isIs_user_do())
        {
            fields.add(TScene._Fields.UTIME.getFieldName());
            update_scene.setUtime(getCurrentTimeStamp());
        }

        //  4.获取更新内容
        BasicDBObject update_scene_db = getUpdateDBObj(req.getFields(), update_scene);

        if (req.isIs_user_do())
        {
            update_scene_db.put("stat.recent_event", constConstants.RECENT_EVENT_SCENE);
            update_scene_db.put("stat.recent_time", getCurrentTimeStamp());
            update_scene_db.put("stat.recent_user", req.getScene().getOwner_id());
        }

        WriteResult wr =
            mongo.update(Tables.Scene, new BasicDBObject(TScene._Fields._ID.getFieldName(), req.get_id()), new BasicDBObject("$set", update_scene_db), false, false);

        logger.debug("writeResult: {},update: {} trace_tag: {}", wr, update_scene_db, req.getTrace_tag());
        return res;
    }

    /**
     * 删除现场 ； 同时删除现场关联的日记、商品、回复。
     */
    @Override
    public DeleteResponse DeleteScene(DeleteSceneRequest req) throws TException
    {
        DeleteResponse resp = fakeDelete(Tables.Scene, req.get_ids(), req.getTrace_tag());

        if ("succ".equals(resp.getRes()))
        {
            BasicDBObject query = new BasicDBObject("scene_id", new BasicDBObject(IN, req.get_ids()));
            query.put("status", ENTITY_STATUS_NORM);
            DBObject oper = new BasicDBObject("$set", new BasicDBObject("status", ENTITY_STATUS_DEL).append("utime", getCurrentTimeStamp()));

            //删除现场下的日记
            mongo.update(Diary, query, oper, false, true);
            //删除现场下的商品
            mongo.update(Product, query, oper, false, true);
            //删除关联的回复
            oper = new BasicDBObject("$set", new BasicDBObject("status", ENTITY_STATUS_DEL));
            mongo.update(Comment, query, oper, false, true);
        }

        return resp;
    }

    /**
     * 查询日记
     */
    @Override
    public ListDiariesResponse ListDiaries(ListDiariesRequest req) throws TException
    {
        List<String> fields = req.getFields();

        ListDiariesResponse res = new ListDiariesResponse();
        res.setTrace_tag(req.getTrace_tag());

        if (null == fields || fields.isEmpty())
        {
            res.setRes("Fields is null! needed fields,please refet to module.thrift!");
            return res;
        }

        if (req.getSortSize() == 0)
            req.setSort(Arrays.asList(new TSortField("ctime", -1)));

        if (req.getStatusSize() == 0)
            req.setStatus(Arrays.asList(ENTITY_STATUS_NORM));

        BasicDBObject query = new BasicDBObject();

        if (fields.contains(ListDiariesRequest._Fields._IDS.getFieldName()))
        {
            if (req.get_idsSize() > 0)
                query.put(TDiary._Fields._ID.getFieldName(), new BasicDBObject(QueryOperators.IN, req.get_ids()));
        }

        if (fields.contains(ListDiariesRequest._Fields.OWNER_IDS.getFieldName()))
        {
            if (req.getOwner_idsSize() > 0)
                query.put(TDiary._Fields.OWNER_ID.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getOwner_ids()));
        }

        if (fields.contains(ListDiariesRequest._Fields.SCENE_IDS.getFieldName()))
        {
            if (req.getScene_idsSize() > 0)
                query.put(TDiary._Fields.SCENE_ID.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getScene_ids()));
        }

        query.put(TDiary._Fields.STATUS.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getStatus()));

        if (fields.contains(ListDiariesRequest._Fields.PROGRESS_IDS.getFieldName()))
        {
            if (req.getProgress_idsSize() > 0)
                query.put(TDiary._Fields.PROGRESS_ID.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getProgress_ids()));
        }

        if (fields.contains(ListDiariesRequest._Fields.TAG_IDS.getFieldName()))
        {
            if (req.getTag_idsSize() > 0)
                query.put(TDiary._Fields.TAG_ID.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getTag_ids()));
        }

        if (fields.contains(ListDiariesRequest._Fields.JIANLI_IDS.getFieldName()))
        {
            if (req.getJianli_idsSize() > 0)
                query.put(TDiary._Fields.JIANLI_ID.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getJianli_ids()));
        }

        if (fields.contains(ListDiariesRequest._Fields.JIANLI_STAGE_ID.getFieldName()))
        {
            if (req.getJianli_stage_idSize() > 0)
                query.put(TDiary._Fields.JIANLI_STAGE_ID.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getJianli_stage_id()));
        }

        if (fields.contains(ListDiariesRequest._Fields.FILTER_RESULT.getFieldName()))
        {
            if (req.getFilter_resultSize() > 0)
                query.put(TDiary._Fields.FILTER_RESULT.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getJianli_stage_id()));
        }

        if (fields.contains(ListDiariesRequest._Fields.PRODUCT_ID //这可以做啊
        .getFieldName()))
        {
            query.put(TDiary._Fields.PRODUCTS.getFieldName(), new BasicDBObject(QueryOperators.IN, Arrays.asList(req.getProduct_id())));
        }

        if (fields.contains(ListDiariesRequest._Fields.PUBLISH_SOURCES.getFieldName()))
        {
            if (req.getPublish_sourcesSize() > 0)
                query.put(TDiary._Fields.PUBLISH_SOURCE.getFieldName(), new BasicDBObject(IN, req.getPublish_sources()));
        }

        if (fields.contains(ListDiariesRequest._Fields.PLATFORMS.getFieldName()))
        {
            if (req.getPlatformsSize() > 0)
                query.put(TDiary._Fields.PLATFORM.getFieldName(), new BasicDBObject(IN, req.getPlatforms()));
        }
        if (fields.contains(ListDiariesRequest._Fields.CTIME_RANGE.getFieldName()))
        {
            TRange ctime_range = req.getCtime_range();
            query.put(TDiary._Fields.CTIME.getFieldName(), new BasicDBObject(GTE, ctime_range.getMini_value()).append(LTE, ctime_range.getMax_value()));
        }

        int offset = req.getOffset();

        int limit = req.getLimit();

        BasicDBObject sort = new BasicDBObject();
        List<TSortField> sort_fields = req.getSort();
        if (null != sort_fields)
        {
            for (TSortField sf : sort_fields)
                sort.put(sf.getField(), sf.getAsc());
        }

        logger.debug("search scene table,trace_tag: {}, query: {}, sort:{}, offset:{}, limit:{}", req.getTrace_tag(), query, sort, offset, limit);

        DBCursor c = Commons.mongo.cursor(Tables.Diary, query, null, sort, offset, limit);

        int total = c.count();

        res.setTotal(total);

        res.setOffset(offset);

        res.setLimit(limit);

        res.setResults(new ArrayList<TDiary>());

        Set<String> allProductIds = new HashSet<String>();
        for (DBObject o : c)
        {
            TDiary diary = ThriftUtil.dbObject2ThriftObject(o, TDiary.class);

            if (diary.getProductsSize() > 0)
            {
                allProductIds.addAll(diary.getProducts());
            }

            res.addToResults(diary);
        }

        /** 2.0请求读取2.5数据    alpha.ren 2015.01.26 */
        if (req.getVersion() < constConstants.VERSION_2_5)
        {
            for (TDiary new_diary : res.getResults())
            {
                if (50 == new_diary.getTag_id())
                {
                    switch (new_diary.getProgress_id())
                    {
                        case 1:
                        {
                            new_diary.setTag_id(4);
                            break;
                        }
                        case 2:
                        {
                            new_diary.setTag_id(8);
                            break;
                        }
                        case 3:
                        {
                            new_diary.setTag_id(11);
                            break;
                        }
                        case 4:
                        {
                            new_diary.setTag_id(16);
                            break;
                        }
                        case 5:
                        {
                            new_diary.setTag_id(20);
                            break;
                        }
                        case 6:
                        {
                            new_diary.setTag_id(32);
                            break;
                        }
                        case 7:
                        {
                            new_diary.setTag_id(38);
                            break;
                        }
                    }
                }
                else if (51 == new_diary.getTag_id())
                {
                    switch (new_diary.getProgress_id())
                    {
                        case 1:
                        {
                            new_diary.setTag_id(501);
                            break;
                        }
                        case 2:
                        {
                            new_diary.setTag_id(502);
                            break;
                        }
                        case 3:
                        {
                            new_diary.setTag_id(503);
                            break;
                        }
                        case 4:
                        {
                            new_diary.setTag_id(504);
                            break;
                        }
                        case 5:
                        {
                            new_diary.setTag_id(505);
                            break;
                        }
                        case 6:
                        {
                            new_diary.setTag_id(506);
                            break;
                        }
                        case 7:
                        {
                            new_diary.setTag_id(507);
                            break;
                        }
                        case 8:
                        {
                            new_diary.setTag_id(508);
                            break;
                        }
                    }
                }
            }
        }
        else if (req.getVersion() == constConstants.VERSION_2_5)
        {
            for (TDiary old_diary : res.getResults())
            {
                if (oldTagsProdSele.contains(old_diary.getTag_id()))
                    old_diary.setTag_id(50);
                else if (oldTagsSsn.contains(old_diary.getTag_id()))
                    old_diary.setTag_id(51);
            }
        }

        if (req.getWith_products_detail() == 1)
        {
            res.setProd_map(getProductDetail(allProductIds));
        }

        return res;
    }

    public Map<String, TProduct> getProductDetail(Set<String> productIds)
    {
        DBObject cond = new BasicDBObject("_id", new BasicDBObject("$in", productIds));
        DBCursor cursor = mongo.cursor(Tables.Product, cond, null, null, 0, 0);
        List<TProduct> products = ThriftUtil.listCursor(cursor, TProduct.class);
        if (products == null)
            return null;
        Map<String, TProduct> productMap = new HashMap<String, TProduct>();
        for (TProduct p : products)
            productMap.put(p.get_id(), p);
        return productMap;
    }

    /**
     * 将多于两个的回车换行全部替换为两个 
     */
    public static String replaceEnter(String src)
    {
        if (src == null)
            return null;
        return src.replaceAll("(\r\n){2,}|(\r){2,}|(\n){2,}", "\r\n\r\n");
    }

    /**
     * 创建日记
     */
    @Override
    public CreateResponse CreateDiary(CreateDiaryRequest req) throws TException
    {
        /** 如果版本号是 移动端 2.5之前的请求，需要做请求适配    by alpha.ren 2015.01.26 */
        if (req.getVersion() < constConstants.VERSION_2_5)
        {
            TDiary req_diary = req.getDiary();
            switch (req_diary.getTag_id())
            {
                case 4:
                case 8:
                case 11:
                case 16:
                case 20:
                case 32:
                case 38:
                {
                    req_diary.setTag_id(50);
                    break;
                }
                case 501:
                case 502:
                case 503:
                case 504:
                case 505:
                case 506:
                case 507:
                case 508:
                {
                    req_diary.setTag_id(51);
                    break;
                }
            }
        }

        String validResult = validDiaryReq(req);
        if (validResult != null)
            return new CreateResponse(validResult, null, req.getTrace_tag());

        TDiaryStat stat = new TDiaryStat();
        stat.setRecent_time(getCurrentTimeStamp());
        stat.setRecent_user(req.getDiary().getOwner_id());
        stat.setRecent_event(constConstants.RECENT_EVENT_DIARY);

        // 获取自增id
        long num = mongo.getAutoincNumber(Tables.Diary, true);
        String _id = num + "";
        stat.set_id(_id);

        // 置初始状态
        TDiary diary = req.getDiary();
        diary.setStat(stat);
        if (diary.getStatus() != constConstants.ENTITY_STATUS_DRAFT) //用户可以设置为草稿状态
            diary.setStatus(constConstants.ENTITY_STATUS_PENDING);

        // 设置日记创建时间
        diary.setCtime(getCurrentTimeStamp());
        diary.setUtime(diary.getCtime());
        if (diary.getOtime() == 0)
            diary.setOtime(diary.getCtime());//用户不设置otime的话，默认就是ctime

        // 是监理草稿
        if (diary.getStatus() == 2 && diary.getJianli_id() > 0)
            diary.setJianli_draft(1);

        setUrlType(diary);

        // 过滤多个换行 
        diary.setContent(replaceEnter(diary.getContent()));

        CreateResponse resp = create(Tables.Diary, diary, _id, req.getTrace_tag());

        if ("succ".equals(resp.getRes()))
        {
            req.getDiary().set_id(_id);
            StatWriter.sendMsg(req);
        }
        return resp;
    }

    /**
     * 更新日记
     */
    @Override
    public UpdateResponse UpdateDiary(UpdateDiaryRequest req) throws TException
    {
        UpdateResponse res = new UpdateResponse();
        res.setTrace_tag(req.getTrace_tag());

        List<String> fields = req.getFields();

        //  1.检验未填内容
        if (StringUtil.isEmpty(req.get_id()) || null == fields || fields.isEmpty())
        {
            res.setRes("needed fields,please refet to module.thrift!");
            return res;
        }
        //  2.过滤非法内容
        if (fields.contains(TDiary._Fields._ID.getFieldName())
            || fields.contains(TDiary._Fields.SCENE_ID.getFieldName())
            || fields.contains(TDiary._Fields.IMAGE_NUM.getFieldName())
            || fields.contains(TDiary._Fields.CTIME.getFieldName())
            || fields.contains(TDiary._Fields.UTIME.getFieldName())
            || fields.contains(TDiary._Fields.ORIGIN_ID.getFieldName())
            || fields.contains(TDiary._Fields.STAT.getFieldName()))
        {
            res.setRes("illegel fields,please refet to module.thrift!");
            return res;
        }

        //   3.过滤非法用户
        if (req.getNeed_validate() > 0)
        {
            DBObject origin_diary_db = mongo.findById(Tables.Diary, req.get_id());

            if (null != origin_diary_db)
            {
                TDiary origin_diary = ThriftUtil.dbObject2ThriftObject(origin_diary_db, TDiary.class);
                if (!StringUtil.equals(req.getUser_id(), origin_diary.getOwner_id()))
                {
                    res.setRes("unvalidated user!");
                    return res;
                }
            }
        }

        TDiary update_diary = req.getDiary();

        // 过滤多个换行 
        update_diary.setContent(replaceEnter(update_diary.getContent()));

        if (req.isIs_user_do())
        {
            // 3.升级更新时间
            fields.add(TDiary._Fields.UTIME.getFieldName());
            update_diary.setUtime(getCurrentTimeStamp());
        }

        if (fields.contains(TDiary._Fields.IMAGES.getFieldName()))
        {
            fields.add(TDiary._Fields.IMAGE_NUM.getFieldName());
            // 设置图片数
            update_diary.setImage_num(update_diary.getImagesSize());

        }

        /** 如果版本号是 移动端 2.5之前的请求，需要做请求适配    by alpha.ren 2015.01.26 */
        if (req.getVersion() < constConstants.VERSION_2_5)
        {
            switch (update_diary.getTag_id())
            {
                case 4:
                case 8:
                case 11:
                case 16:
                case 20:
                case 32:
                case 38:
                {
                    update_diary.setTag_id(50);
                    break;
                }
                case 501:
                case 502:
                case 503:
                case 504:
                case 505:
                case 506:
                case 507:
                case 508:
                {
                    update_diary.setTag_id(51);
                    break;
                }
            }
        }

        //  4.更新数据
        BasicDBObject update_diary_db = getUpdateDBObj(req.getFields(), update_diary);
        int status = update_diary.getStatus();
        if (status != ENTITY_STATUS_DRAFT) //不是草稿状态，就设置为待审核状态，其他状态不能由传入而是由审核决定
            update_diary_db.put(TDiary._Fields.STATUS.getFieldName(), ENTITY_STATUS_PENDING);

        if (req.isIs_user_do())
        {
            update_diary_db.put("stat.recent_event", constConstants.RECENT_EVENT_SCENE);
            update_diary_db.put("stat.recent_time", getCurrentTimeStamp());
            update_diary_db.put("stat.recent_user", req.getDiary().getOwner_id());
        }

        WriteResult wr =
            Commons.mongo.update(Tables.Diary, new BasicDBObject(TDiary._Fields._ID.getFieldName(), req.get_id()), new BasicDBObject("$set", update_diary_db), false, false);

        logger.debug("update diary trace_tag: {},update: {}, write result: {}", req.getTrace_tag(), update_diary_db, wr);

        this.traceThrift(res);

        if (wr != null && wr.getN() > 0)
        {
            DBObject obj = mongo.findById(Tables.Diary, req.get_id());
            String sceneId = obj.get("scene_id").toString();
            req.getDiary().setScene_id(sceneId);
            req.getDiary().set_id(req.get_id());
            // 发送消息，更新日记信息
            StatWriter.sendMsg(req);
        }
        return res;
    }

    /**
     * 删除日记，同时删除日记下面关联的回复。
     */
    @Override
    public DeleteResponse DeleteDiary(DeleteDiaryRequest req) throws TException
    {
        DeleteResponse resp = new DeleteResponse();
        //   1.过滤非法用户
        if (req.getNeed_validate() > 0)
        {
            if (req.getDiary_idsSize() > 0)
            {
                for (String diary_id : req.getDiary_ids())
                {
                    DBObject origin_diary_db = mongo.findById(Tables.Diary, diary_id);

                    if (null != origin_diary_db)
                    {
                        TDiary origin_diary = ThriftUtil.dbObject2ThriftObject(origin_diary_db, TDiary.class);
                        if (!StringUtil.equals(req.getUser_id(), origin_diary.getOwner_id()))
                        {
                            resp.setRes("unvalidated user on diary " + origin_diary.get_id() + "!");
                            return resp;
                        }
                    }
                }
            }
        }

        resp = fakeDelete(Tables.Diary, req.getDiary_ids(), req.getTrace_tag());
        if ("succ".equals(resp.getRes()))
        {
            //删除关联的回复
            BasicDBObject query = new BasicDBObject(TComment._Fields.DIARY_ID.getFieldName(), new BasicDBObject(IN, req.getDiary_ids()));
            query.put(TComment._Fields.TGT_TYPE.getFieldName(), new BasicDBObject(IN, Arrays.asList(TARGET_TYPE_DIARY, TARGET_TYPE_COMMENT)));
            DBObject oper = new BasicDBObject("$set", new BasicDBObject(TComment._Fields.STATUS.getFieldName(), ENTITY_STATUS_DEL));
            WriteResult ws = mongo.update(Comment, query, oper, false, true);

            logger.debug("delete diary trace_tag: {}, n related: {}", req.getTrace_tag(), ws.getN());

            // 刷新统计信息
            StatWriter.sendMsg(req);
        }
        return resp;
    }

    @Override
    public ListProductResponse ListProduct(ListProductRequest req) throws TException
    {
        List<String> fields = req.getFields();

        ListProductResponse res = new ListProductResponse();
        res.setTrace_tag(req.getTrace_tag());

        if (null == fields || fields.isEmpty())
        {
            res.setRes("needed fields,please refet to module.thrift!");
            return res;
        }

        if (req.getSortSize() == 0)
            req.setSort(Arrays.asList(new TSortField("ctime", -1)));

        if (req.getStatusSize() == 0)
            req.setStatus(Arrays.asList(ENTITY_STATUS_NORM));

        BasicDBObject query = new BasicDBObject();

        if (fields.contains(ListProductRequest._Fields._IDS.getFieldName()))
        {
            if (req.get_idsSize() > 0)
                query.put(TProduct._Fields._ID.getFieldName(), new BasicDBObject(QueryOperators.IN, req.get_ids()));
        }

        if (fields.contains(ListProductRequest._Fields.SCENE_IDS.getFieldName()))
        {
            if (req.getScene_idsSize() > 0)
                query.put(TProduct._Fields.SCENE_ID.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getScene_ids()));
        }

        query.put(TProduct._Fields.STATUS.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getStatus()));

        if (fields.contains(ListProductRequest._Fields.FILTER_RESULTS.getFieldName()))
        {
            if (req.getFilter_resultsSize() > 0)
                query.put(TProduct._Fields.FILTER_RESULT.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getFilter_results()));
        }

        if (fields.contains(ListProductRequest._Fields.FILTER_RESULTS.getFieldName()))
        {
            if (req.getFilter_resultsSize() > 0)
                query.put(TProduct._Fields.FILTER_RESULT.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getFilter_results()));
        }

        if (fields.contains(ListProductRequest._Fields.BUY_FROM_1.getFieldName()))
        {
            query.put(TProduct._Fields.BUY_FROM_1.getFieldName(), req.getBuy_from_1());
        }

        if (fields.contains(ListProductRequest._Fields.BUY_FROM_2.getFieldName()))
        {
            query.put(TProduct._Fields.BUY_FROM_2.getFieldName(), req.getBuy_from_2());
        }

        if (fields.contains(ListProductRequest._Fields.BUY_FROM_NAME.getFieldName()))
        {
            query.put(TProduct._Fields.BUY_FROM_NAME.getFieldName(), req.getBuy_from_name());
        }

        if (fields.contains(ListProductRequest._Fields.CATEGORY_1.getFieldName()))
        {
            query.put(TProduct._Fields.CATEGORY_1.getFieldName(), req.getCategory_1());
        }

        if (fields.contains(ListProductRequest._Fields.CATEGORY_2.getFieldName()))
        {
            query.put(TProduct._Fields.CATEGORY_2.getFieldName(), req.getCategory_2());
        }

        if (fields.contains(ListProductRequest._Fields.CTIME_RANGE.getFieldName()))
        {
            TRange ctime_range = req.getCtime_range();
            query.put(TProduct._Fields.CTIME.getFieldName(), new BasicDBObject(GTE, ctime_range.getMini_value()).append(LTE, ctime_range.getMax_value()));
        }

        int offset = req.getOffset();

        int limit = req.getLimit();

        BasicDBObject sort = new BasicDBObject();
        List<TSortField> sort_fields = req.getSort();
        if (null != sort_fields)
        {
            for (TSortField sf : sort_fields)
                sort.put(sf.getField(), sf.getAsc());
        }

        logger.debug("search trace_tag:{} product table, query: " + query + ", sort:" + sort + ", offset:" + offset + ", limit:" + limit, req.getTrace_tag());

        DBCursor c = mongo.cursor(Tables.Product, query, null, sort, offset, limit);

        int total = c.count();

        res.setTotal(total);

        res.setOffset(offset);

        res.setLimit(limit);

        res.setResults(new ArrayList<TProduct>());

        for (DBObject o : c)
        {
            TProduct product = ThriftUtil.dbObject2ThriftObject(o, TProduct.class);
            res.addToResults(product);
        }

        if (req.getShow_diarys() > 0)
        {
            for (TProduct prod : res.getResults())
            {
                List<String> diary_list = prod.getDiary_list();
                DBCursor cur =
                    mongo.cursor(Tables.Diary, new BasicDBObject(TDiary._Fields.PRODUCTS.getFieldName(), new BasicDBObject(QueryOperators.IN, Arrays.asList(new String[] { prod.get_id() }))), new BasicDBObject(TDiary._Fields.CTIME.getFieldName(), 1).append(TDiary._Fields.STATUS.getFieldName(), 1), null, 0, 0);

                logger.debug("find trace_tag {},{} diaries for prod {}", req.getTrace_tag(), cur.count(), prod.get_id());

                if (null == diary_list)
                {
                    diary_list = new ArrayList<String>();
                    prod.setDiary_list(diary_list);
                }

                while (cur.hasNext())
                {
                    BasicDBObject diary_db = (BasicDBObject) cur.next();
                    if (null == diary_db)
                        continue;
                    logger.debug("find trace_tag {},diary:{} ,diary_id:{}", req.getTrace_tag(), diary_db, diary_db.getString("_id"));
                    if (!req.getStatus().contains(diary_db.getInt("status")))
                        continue;
                    diary_list.add(diary_db.getString("_id"));
                }
            }
        }

        return res;
    }

    /**
     * 将产品id追加到diary的products中
     * @param diary_id 日记id
     * @param product_id 商品id
     */
    public void appendProductToDiary(String diary_id, String product_id)
    {
        DBObject cond = new BasicDBObject("_id", diary_id);
        DBObject oper = new BasicDBObject("$push", new BasicDBObject("products", product_id));
        mongo.update(Tables.Diary, cond, oper, false, false);
    }

    /**
     * 创建商品
     */
    @Override
    public CreateResponse CreateProduct(CreateProductRequest req) throws TException
    {
        String validResult = validProductReq(req);
        if (validResult != null)
            return new CreateResponse(validResult, null, req.getTrace_tag());

        TProduct product = req.getProduct();

        /** 添加owner_id */
        if (!StringUtil.isEmpty(product.getScene_id()))
        {
            TScene scene = ThriftUtil.dbObject2ThriftObject(mongo.findById(Tables.Scene, product.getScene_id()), TScene.class);
            if (null != scene)
                product.setOwner_id(scene.getOwner_id());
        }

        product.setCtime(getCurrentTimeStamp());
        product.setUtime(req.getProduct().getCtime());
        product.setStatus(ENTITY_STATUS_PENDING);
        if (0 == product.getTotal_price())
            product.setTotal_price(product.getUnit_price() * product.getNum());

        CreateResponse resp = create(Tables.Product, req.getProduct(), req.getTrace_tag());
        if (!StringUtil.isEmpty(req.getDiary_id()))
        {
            // 增加日记和商品的关联关系
            appendProductToDiary(req.getDiary_id(), resp.get_id());
        }
        if ("succ".equals(resp.getRes()))
        {
            // 设置id，以便 消息队列的统计
            req.getProduct().set_id(resp.get_id());
            StatWriter.sendMsg(req);
        }
        return resp;
    }

    @Override
    public UpdateResponse UpdateProduct(UpdateProductRequest req) throws TException
    {

        UpdateResponse res = new UpdateResponse();
        res.setTrace_tag(req.getTrace_tag());

        TProduct update_product = req.getProduct();

        List<String> fields = req.getFields();

        if (StringUtil.isEmpty(req.get_id()) || null == fields || fields.isEmpty())
        {
            res.setRes("needed fields,please refet to module.thrift!");
            return res;
        }
        if (fields.contains(TProduct._Fields._ID.getFieldName())
            || fields.contains(TProduct._Fields.CTIME.getFieldName())
            || fields.contains(TProduct._Fields.UTIME.getFieldName()))
        {
            res.setRes("illegel fields,please refet to module.thrift!");
            return res;
        }

        if (req.getNeed_validate() > 0)
        {
            DBObject origin_product_db = mongo.findById(Tables.Product, req.get_id());

            if (null != origin_product_db)
            {
                TProduct origin_product = ThriftUtil.dbObject2ThriftObject(origin_product_db, TProduct.class);
                if (!StringUtil.equals(req.getUser_id(), origin_product.getOwner_id()))
                {
                    res.setRes("unvalidated user!");
                    return res;
                }
            }
        }

        if (req.isIs_user_do())
        {
            fields.add(TProduct._Fields.UTIME.getFieldName());
            update_product.setUtime(getCurrentTimeStamp());
        }

        BasicDBObject update_product_db = getUpdateDBObj(req.getFields(), update_product);
        update_product_db.remove(TProduct._Fields._ID.getFieldName());

        // 更新后自动设置为待审核
        update_product_db.put(TProduct._Fields.STATUS.getFieldName(), constConstants.ENTITY_STATUS_PENDING);

        WriteResult wr =
            Commons.mongo.update(Tables.Product, new BasicDBObject(TProduct._Fields._ID.getFieldName(), req.get_id()), new BasicDBObject("$set", update_product_db), false, false);

        logger.debug("update product: result:{},update:{},trace_tag:{}", wr, update_product_db, req.getTrace_tag());

        if (wr != null && wr.getN() > 0)
            StatWriter.sendMsg(req);// 发送消息，更新清单信息
        return res;
    }

    /**
     * 将产品Id和日记的products字段中移除
     * @param productId 产品Id
     * @param diaryId 日记Id
     */
    public void pullProductFromDiary(String productId, String diaryId)
    {
        DBObject cond = new BasicDBObject("_id", diaryId);
        DBObject oper = new BasicDBObject("$pull", new BasicDBObject("products", productId));
        mongo.update(Tables.Diary, cond, oper, false, false);
    }

    @Override
    public DeleteResponse DeleteProduct(DeleteProductRequest req) throws TException
    {
        DeleteResponse resp = new DeleteResponse();
        //   1.过滤非法用户
        if (req.getNeed_validate() > 0)
        {
            if (req.get_idsSize() > 0)
            {
                for (String product_id : req.get_ids())
                {
                    DBObject origin_product_db = mongo.findById(Tables.Product, product_id);

                    if (null != origin_product_db)
                    {
                        TProduct origin_product = ThriftUtil.dbObject2ThriftObject(origin_product_db, TProduct.class);
                        if (!StringUtil.equals(req.getUser_id(), origin_product.getOwner_id()))
                        {
                            resp.setRes("unvalidated user on product " + origin_product.get_id() + "!");
                            return resp;
                        }
                    }
                }
            }
        }

        resp = fakeDelete(Tables.Product, req.get_ids(), req.getTrace_tag());
        if ("succ".equals(resp.getRes()))
        {
            // 从相关日记中移除
            for (String productId : req.get_ids())
            {
                List<String> diaryIds = getUsedProductDiary(productId);
                for (String diaryId : diaryIds)
                {
                    pullProductFromDiary(productId, diaryId);
                }
            }
            StatWriter.sendMsg(req);
        }
        return resp;
    }

    public List<String> getUsedProductDiary(String productId)
    {
        DBObject cond = new BasicDBObject(TDiary._Fields.PRODUCTS.getFieldName(), new BasicDBObject(IN, Arrays.asList(productId)));
        DBObject fields = new BasicDBObject("_id", 1);
        DBCursor cursor = mongo.cursor(Tables.Diary, cond, fields, null, 0, 0);
        List<String> diaryIds = new ArrayList<String>();
        for (DBObject dbObject : cursor)
        {
            Object id = dbObject.get("_id");
            diaryIds.add(String.valueOf(id));
        }
        return diaryIds;
    }

    /**
     * 查询回复，包括回复下面的回复
     */
    @Override
    public ListCommentsResponse ListComments(ListCommentsRequest req) throws TException
    {
        List<String> fields = req.getFields();
        ListCommentsResponse res = new ListCommentsResponse();
        res.setTrace_tag(req.getTrace_tag());

        int total_comment_num = 0;
        int ref_total_comment_num = 0;

        if (null == fields || fields.isEmpty())
        {
            res.setRes("Fields is Null!");
            return res;
        }

        if (req.getSortSize() == 0)
            req.setSort(Arrays.asList(new TSortField("ctime", -1)));

        if (req.getStatusSize() == 0)
            req.setStatus(Arrays.asList(ENTITY_STATUS_NORM));

        BasicDBObject query = new BasicDBObject();
        if (fields.contains("scene_id") || fields.contains("diary_id"))
            query.put(TComment._Fields.REF_COMMENT_ID.getFieldName(), new BasicDBObject("$in", Arrays.asList(null, "0", "")));
        else
            req.setTgt_type(Arrays.asList(1, 2, 3, 4));

        if (fields.contains(ListCommentsRequest._Fields._IDS.getFieldName()) && req.get_idsSize() > 0)
            query.put(TComment._Fields._ID.getFieldName(), new BasicDBObject(IN, req.get_ids()));

        if (fields.contains(ListCommentsRequest._Fields.SCENE_ID.getFieldName()) && req.getScene_idSize() > 0)
            query.put(TComment._Fields.SCENE_ID.getFieldName(), new BasicDBObject(IN, req.getScene_id()));

        if (fields.contains(ListCommentsRequest._Fields.DIARY_ID.getFieldName()) && req.getDiary_idSize() > 0)
            query.put(TComment._Fields.DIARY_ID.getFieldName(), new BasicDBObject(IN, req.getDiary_id()));

        if (fields.contains(ListCommentsRequest._Fields.USER_ID.getFieldName()) && req.getUser_idSize() > 0)
            query.put(TComment._Fields.USER_ID.getFieldName(), new BasicDBObject(IN, req.getUser_id()));

        query.put(TComment._Fields.STATUS.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getStatus()));

        if (fields.contains(ListCommentsRequest._Fields.TGT_TYPE.getFieldName()) && req.getTgt_typeSize() > 0)
        {
            query.put(TComment._Fields.TGT_TYPE.getFieldName(), new BasicDBObject(IN, req.getTgt_type()));
        }

        if (fields.contains(ListCommentsRequest._Fields.CTIME_RANGE.getFieldName()))
        {
            TRange ctime_range = req.getCtime_range();
            query.put(TComment._Fields.CTIME.getFieldName(), new BasicDBObject(GTE, ctime_range.getMini_value()).append(LTE, ctime_range.getMax_value()));
        }

        int offset = req.getOffset();
        int limit = req.getLimit();

        BasicDBObject sort = (BasicDBObject) sortField(req.getSort());

        logger.trace("search comment table, trace:"
            + req.getTrace_tag()
            + " query: "
            + query
            + ", sort:"
            + sort
            + ", offset:"
            + offset
            + ", limit:"
            + limit);

        DBCursor c = Commons.mongo.cursor(Tables.Comment, query, null, sort, offset, limit);

        res.setOffset(offset);
        res.setLimit(limit);
        List<TCommentView> commentViews = Lists.newArrayList();
        res.setResults(commentViews);

        if (c.size() == 0)
            return res;

        List<TComment> comments = new ArrayList<TComment>();
        for (DBObject o : c)
            comments.add(ThriftUtil.dbObject2ThriftObject(o, TComment.class));

        List<String> replyCommentIds = new ArrayList<String>();
        List<String> commentDiaryIds = new ArrayList<String>();
        for (TComment comm : comments)
        {
            replyCommentIds.add(comm.get_id());
            commentDiaryIds.add(comm.getDiary_id());
        }

        //如果是查询日记或者现场的回复，则分别从各自的统计值中获取个数，而不再count()计算
        if ((fields.contains("diary_id") && !fields.contains("scene_id") && req.getTgt_typeSize() > 0 && req.getTgt_type().contains(TARGET_TYPE_DIARY)))
        {
            DBCursor diary_cur =
                Commons.mongo.cursor(Tables.Diary, new BasicDBObject(TDiary._Fields._ID.getFieldName(), new BasicDBObject(IN, req.getDiary_id())), null, null, 0, 0);
            while (diary_cur.hasNext())
            {
                DBObject diary_db = diary_cur.next();
                if (null == diary_db)
                    continue;
                TDiary diary = ThriftUtil.dbObject2ThriftObject(diary_db, TDiary.class);
                if (null != diary && null != diary.getStat())
                {
                    total_comment_num += diary.getStat().getComment_num();
                    ref_total_comment_num += diary.getStat().getRef_comment_num();
                }
            }
            logger.debug("count comment:{}", 1);
        }
        else if (fields.contains("scene_id")
            && !fields.contains("diary_id")
            && req.getTgt_typeSize() > 0
            && req.getTgt_type().contains(TARGET_TYPE_SCENE))
        {
            DBCursor scene_cur =
                Commons.mongo.cursor(Tables.Scene, new BasicDBObject(TScene._Fields._ID.getFieldName(), new BasicDBObject(IN, req.getScene_id())), null, null, 0, 0);
            while (scene_cur.hasNext())
            {
                DBObject scene_db = scene_cur.next();
                if (null == scene_db)
                    continue;
                TScene scene = ThriftUtil.dbObject2ThriftObject(scene_db, TScene.class);
                if (null != scene && null != scene.getStat())
                {
                    total_comment_num += scene.getStat().getComment_num();
                    ref_total_comment_num += scene.getStat().getRef_comment_num();
                }
            }
            logger.debug("count comment:{}", 2);
        }
        else
        {
            ref_total_comment_num = c.count();
            //统计全部的回复，去除限制进行统计
            query.remove("ref_comment_id");
            if (req.getTgt_typeSize() > 0 && !req.getTgt_type().contains(TARGET_TYPE_COMMENT))
            {
                req.getTgt_type().add(TARGET_TYPE_COMMENT);
                query.put("tgt_type", new BasicDBObject(IN, req.getTgt_type()));
            }
            total_comment_num = Commons.mongo.cursor(Tables.Comment, query, null, sort, offset, limit).count();
        }

        //查询回复下关联的回复, 回复的回复默认排序为时间正序
        BasicDBObject queryReply = new BasicDBObject(TComment._Fields.REF_COMMENT_ID.getFieldName(), new BasicDBObject(IN, replyCommentIds));
        queryReply.put(TComment._Fields.STATUS.getFieldName(), ENTITY_STATUS_NORM);
        DBCursor replyCursor = mongo.cursor(Tables.Comment, queryReply, null, new BasicDBObject("ctime", 1), 0, 0);

        List<TComment> replyComments = new ArrayList<TComment>();
        for (DBObject o : replyCursor)
            replyComments.add(ThriftUtil.dbObject2ThriftObject(o, TComment.class));

        //查询出每个回复关联的日记对象
        ListDiariesRequest commRelateDiariesReq = new ListDiariesRequest();
        commRelateDiariesReq.setFields(Arrays.asList(ListDiariesRequest._Fields._IDS.getFieldName(), ListDiariesRequest._Fields.STATUS.getFieldName()));
        commRelateDiariesReq.setStatus(Arrays.asList(ENTITY_STATUS_NORM, ENTITY_STATUS_PENDING));
        commRelateDiariesReq.set_ids(commentDiaryIds);
        commRelateDiariesReq.setLimit(0);
        commRelateDiariesReq.setVersion(req.getVersion());
        ListDiariesResponse commRelateDiariesResp = ListDiaries(commRelateDiariesReq);

        //将回复和 回复下面的回复 、 日记 的关系关联起来，避免多次查询数据库
        Map<String, TDiary> commRelateDiaryMap = new HashMap<String, TDiary>();
        Map<String, List<TComment>> commReplyCommMap = new HashMap<String, List<TComment>>();
        for (TComment comment : comments)
        {
            for (TDiary diary : commRelateDiariesResp.getResults())
            {
                if (diary.get_id().equals(comment.getDiary_id()))
                {
                    diary.setContent("");//回复需要日记的阶段等信息，每个回复对象关联一个日记对象；暂时没有使用日记内容，设置为空串，减少数据传输
                    diary.setTag_id(convertTagId(diary.getTag_id(), diary.getProgress_id(), req.getVersion()));
                    commRelateDiaryMap.put(comment.get_id(), diary);
                    break;
                }
            }

            for (TComment replyComm : replyComments)
            {
                if (comment.get_id().equals(replyComm.getRef_comment_id()))
                {
                    List<TComment> list = commReplyCommMap.get(comment.get_id());
                    if (null == list)
                    {
                        list = new ArrayList<TComment>();
                        commReplyCommMap.put(comment.get_id(), list);
                    }
                    list.add(replyComm);
                }
            }
        }

        for (TComment comment : comments)
        {
            TCommentView view = new TCommentView();
            view.setComment(comment);
            view.setReply_comment(commReplyCommMap.get(comment.get_id()));
            view.setReply_diary(commRelateDiaryMap.get(comment.get_id()));
            commentViews.add(view);
        }

        res.setResults(commentViews);
        res.setTotal(total_comment_num);
        res.setRef_total(ref_total_comment_num);
        return res;
    }

    private static int convertTagId(int oldTagId, int progressId, double version)
    {
        if (version < constConstants.VERSION_2_5)
        {
            if (50 == oldTagId)
            {
                switch (progressId)
                {
                    case 1:
                    {
                        return 4;
                    }
                    case 2:
                    {
                        return 8;
                    }
                    case 3:
                    {
                        return 11;
                    }
                    case 4:
                    {
                        return 16;
                    }
                    case 5:
                    {
                        return 20;
                    }
                    case 6:
                    {
                        return 32;
                    }
                    case 7:
                    {
                        return 38;
                    }
                }
            }
            else if (51 == oldTagId)
            {
                switch (progressId)
                {
                    case 1:
                    {
                        return 501;
                    }
                    case 2:
                    {
                        return 502;
                    }
                    case 3:
                    {
                        return 503;
                    }
                    case 4:
                    {
                        return 504;
                    }
                    case 5:
                    {
                        return 505;
                    }
                    case 6:
                    {
                        return 506;
                    }
                    case 7:
                    {
                        return 507;
                    }
                    case 8:
                    {
                        return 508;
                    }
                }
            }
        }
        else if (version == constConstants.VERSION_2_5)
        {
            if (oldTagsProdSele.contains(oldTagId))
                return 50;
            else if (oldTagsSsn.contains(oldTagId))
                return 51;
        }
        return oldTagId;
    }

    @Override
    public CreateResponse CreateComment(CreateCommentRequest req) throws TException
    {
        String validResult = validCommentReq(req);
        if (validResult != null)
        {
            return new CreateResponse(validResult, null, req.getTrace_tag());
        }

        if (req.getComment().getTgt_type() != constConstants.TARGET_TYPE_COMMENT)
        {
            req.getComment().setRef_comment_id(null);
        }
        else
        {
            //如果前端不传diary_id ，后端根据ref_comment 查出diary_id ，填上   alpha.ren 20141211
            //如果前端不传product_id ，后端根据ref_comment 查出product_id ，填上   alpha.ren 20141211
            TComment comment = req.getComment();
            String ref_comment_id = comment.getRef_comment_id();
            if (!StringUtil.isEmpty(ref_comment_id))
            {
                DBObject ref_comment_db = mongo.findById(Tables.Comment, ref_comment_id);
                if (null != ref_comment_db)
                {
                    TComment ref_comment = ThriftUtil.dbObject2ThriftObject(ref_comment_db, TComment.class);
                    switch (ref_comment.getTgt_type())
                    {
                        case constConstants.TARGET_TYPE_DIARY:
                        {
                            req.getComment().setDiary_id(ref_comment.getDiary_id());
                            break;
                        }
                        case constConstants.TARGET_TYPE_PRODUCT:
                        {
                            //虽然目前没值，但先这样做也不算错，以备后患  alpha.ren  20141211
                            req.getComment().setProduct_id(ref_comment.getProduct_id());
                            break;
                        }
                    }
                }
            }
        }

        req.getComment().setCtime(getCurrentTimeStamp());
        req.getComment().setStatus(constConstants.ENTITY_STATUS_NORM);// 评论不审核

        CreateResponse resp = create(Tables.Comment, req.getComment(), req.getTrace_tag());
        if ("succ".equals(resp.getRes()))
        {
            req.getComment().set_id(resp.get_id());
            StatWriter.sendMsg(req);
        }
        return resp;
    }

    /**
     * 删除回复，回复下面相关的回复全部都进行删除（假删除，状态设置为-1）
     */
    @Override
    public DeleteResponse DeleteComment(DeleteCommentRequest req) throws TException
    {
        if (req.get_idsSize() == 0)
        {
            DeleteResponse resp = new DeleteResponse();
            resp.setRes("Error ! _ids is null !");
            return resp;
        }

        DeleteResponse resp = new DeleteResponse();
        resp.setTrace_tag(req.getTrace_tag());

        for (String id : req.get_ids())
            recursionDeleteComment(id);

        StatWriter.sendMsg(req);

        return resp;
    }

    /**
     * 递归删除回复
     */
    @SuppressWarnings("unchecked")
    private void recursionDeleteComment(String commentId) throws TException
    {
        BasicDBObject comment = (BasicDBObject) mongo.findById(Tables.Comment, commentId);
        if (comment.getInt(TComment._Fields.STATUS.getFieldName()) != ENTITY_STATUS_DEL)
            mongo.updateField(Comment, commentId, "status", ENTITY_STATUS_DEL);

        DBObject oper = new BasicDBObject("$set", new BasicDBObject(TComment._Fields.STATUS.getFieldName(), ENTITY_STATUS_DEL));
        if (comment.get("ref_comment_id") == null || StringUtil.isEmpty(comment.getString("ref_comment_id")))
        {
            //ref_comment_id为null，则说明为主回复,删除下面全部的回复
            BasicDBObject query = new BasicDBObject(TComment._Fields.REF_COMMENT_ID.getFieldName(), commentId);
            query.put(TComment._Fields.STATUS.getFieldName(), ENTITY_STATUS_NORM);
            mongo.update(Tables.Comment, query, oper, false, true);
        }
        else
        {
            BasicDBObject query = new BasicDBObject(TComment._Fields.PARENT_COMMENT_ID.getFieldName(), commentId);
            query.put(TComment._Fields.STATUS.getFieldName(), ENTITY_STATUS_NORM);
            WriteResult ws = mongo.update(Tables.Comment, query, oper, false, true);
            if (ws.getN() > 0) //说明该回复下面还有回复，需要递归删除
            {
                query.remove(TComment._Fields.STATUS.getFieldName());
                List<String> ids = mongo.distinct(Tables.Comment, query, TComment._Fields._ID.getFieldName());
                for (String id : ids)
                    recursionDeleteComment(id);
            }
        }
    }

    String Behave(TBehavior behavior) throws TException
    {
        // 如果是浏览事件，则只需要发一条消息，通知增现场的浏览数， 不需要保存这个事件
        if (behavior.getBhv_type() == constConstants.BEHAVIOR_TYPE_VIEW)
        {
            StatWriter.sendMsg(behavior);
            return "0";
        }

        behavior.setCtime(getCurrentTimeStamp());
        DBObject cond = new BasicDBObject();
        cond.put(TBehavior._Fields.USER_ID.getFieldName(), behavior.getUser_id());
        cond.put(TBehavior._Fields.BHV_TYPE.getFieldName(), behavior.getBhv_type());
        cond.put(TBehavior._Fields.TGT_ID.getFieldName(), behavior.getTgt_id());
        cond.put(TBehavior._Fields.TGT_TYPE.getFieldName(), behavior.getTgt_type());
        cond.put(TBehavior._Fields.STATUS.getFieldName(), behavior.getStatus());

        DBObject dbObject = thriftObject2DBObject(behavior);
        String id = mongo.getAutoincNumber(Tables.Behavior, true) + "";
        dbObject.put("_id", id);
        DBObject set = new BasicDBObject("$set", dbObject);

        // 再插入(upsert),之所以不用insert是考虑到并发时，相同条件可能会插入多条
        WriteResult wr = null;
        // 判断是否是重复数据
        boolean exist = mongo.exist(Tables.Behavior, cond);
        try
        {
            // 把status条件移除
            cond.removeField(TBehavior._Fields.STATUS.getFieldName());
            // 先删除
            mongo.remove(Tables.Behavior, cond);
            wr = mongo.update(Tables.Behavior, cond, set, true, false);
        }
        catch (Exception e)
        {
            logger.error("Behave Exception！{} \n {}", e, LogUtil.logStackTrace(e));
        }

        if (wr != null && !exist) // 如果库中确定存在数据，说明是重复数据，则不需要发送消息队列更新
            StatWriter.sendMsg(behavior);
        return id;
    }

    /**
     * 记录用户行为
     */
    @Override
    public BehaveResponse Behave(BehaveRequest req) throws TException
    {
        String validResult = validBehaveReq(req);
        BehaveResponse bhv_res = new BehaveResponse();
        bhv_res.setTrace_tag(req.getTrace_tag());

        if (validResult != null)
        {
            bhv_res.setRes(validResult);
            return bhv_res;
        }

        List<String> succ_ids = new ArrayList<String>();
        List<String> fail_ids = new ArrayList<String>();

        for (TBehavior behavior : req.getBehaviors())
        {
            String id = Behave(behavior);
            if (null == id)
                fail_ids.add("");
            else
                succ_ids.add(id);
        }
        if (succ_ids.isEmpty())
            bhv_res.setRes("succ partially");
        else
            bhv_res.setRes("succ");

        bhv_res.setSucc_ids(succ_ids);
        bhv_res.setFail_ids(fail_ids);

        return bhv_res;
    }

    @Override
    public ListBehaviorResponse ListBehavior(ListBehaviorRequest req) throws TException
    {
        String validResult = validListBehaviorRequest(req);
        if (validResult != null)
            return new ListBehaviorResponse(validResult, 0, null, req.getTrace_tag());

        DBObject cond = new BasicDBObject();

        for (String field : req.getFields())
        {
            try
            {
                Field f = req.getClass().getField(field);
                f.setAccessible(true);
                Object val = f.get(req);
                cond.put(field, val);
            }
            catch (Exception e)
            {
                return new ListBehaviorResponse(e.getMessage(), 0, null, req.getTrace_tag());
            }
        }

        ListBehaviorResponse resp = new ListBehaviorResponse();
        DBCursor cursor = mongo.cursor(Tables.Behavior, cond, null, null, 0, 0);
        resp.setTotal(cursor.size());
        resp.setResults(listCursor(cursor, TBehavior.class));
        return resp;
    }

    /**
     * 查询消息接口
     */
    @Override
    public ListMessagesResponse ListMessages(ListMessagesRequest req) throws TException
    {
        List<String> fields = req.getFields();
        ListMessagesResponse resp = new ListMessagesResponse();
        resp.setTrace_tag(req.getTrace_tag());

        if (null == fields || fields.size() == 0)
        {
            resp.setRes("Fail! fields is null !");
            return resp;
        }

        BasicDBObject query = new BasicDBObject();

        //tgt_type=4说明是回复的回复，V2.0版本中回复的回复不产生消息，旧版本数据中存在回复的回复。暂时先过滤掉，如果以后回复的回复也产生消息，再取消过滤
        query.put(TMessage._Fields.TGT_TYPE.getFieldName(), new BasicDBObject(NE, constConstants.TARGET_TYPE_COMMENT));

        if (fields.contains(ListMessagesRequest._Fields.SRC_UID.getFieldName()) && !StringUtil.isEmpty(req.getSrc_uid()))
            query.put(ListMessagesRequest._Fields.SRC_UID.getFieldName(), req.getSrc_uid());

        if (fields.contains(ListMessagesRequest._Fields.TGT_UID.getFieldName()) && !StringUtil.isEmpty(req.getTgt_uid()))
            query.put(ListMessagesRequest._Fields.TGT_UID.getFieldName(), req.getTgt_uid());

        if (fields.contains(ListMessagesRequest._Fields.TGT_READ.getFieldName()))
            query.put(ListMessagesRequest._Fields.TGT_READ.getFieldName(), req.getTgt_read());

        if (fields.contains(ListMessagesRequest._Fields.TGT_DEL.getFieldName()))
            query.put(ListMessagesRequest._Fields.TGT_DEL.getFieldName(), req.getTgt_del());

        if (fields.contains(ListMessagesRequest._Fields.TGT_TYPE.getFieldName()))
            query.put(ListMessagesRequest._Fields.TGT_TYPE.getFieldName(), req.getTgt_type());

        if (fields.contains(ListMessagesRequest._Fields.BHV_TYPE.getFieldName()))
            query.put(ListMessagesRequest._Fields.BHV_TYPE.getFieldName(), req.getBhv_type());

        logger.trace("search scene table, query: " + query);

        BasicDBObject sort = new BasicDBObject(TMessage._Fields.CTIME.getFieldName(), -1);
        DBCursor cursor = Commons.mongo.cursor(Tables.Message, query, null, sort, req.getOffset(), req.getLimit());

        resp.setTotal(cursor.count());
        resp.setOffset(req.getOffset());
        resp.setLimit(req.getLimit());
        resp.setResults(new ArrayList<TMessage>());

        for (DBObject obj : cursor)
        {
            TMessage message = ThriftUtil.dbObject2ThriftObject(obj, TMessage.class);
            resp.addToResults(message);
        }

        return resp;
    }

    /**
     * 更新消息状态。注：消息现在只能更新状态：tgt_read、tgt_del
     */
    @Override
    public UpdateMessageResponse UpdateMessage(UpdateMessageRequest req) throws TException
    {
        traceThrift(req);

        UpdateMessageResponse resp = new UpdateMessageResponse();

        List<String> fields = req.getFields();
        if (null == fields || fields.size() == 0)
        {
            resp.setRes("Fail! Update-Fields is null!");
            return resp;
        }
        if (null == req.getIds() || req.getIds().size() == 0)
        {
            resp.setRes("Fial! Message Id is null!");
            return resp;
        }

        BasicDBObject udpateMessage = getUpdateDBObj(req.getFields(), req.getMessage());
        WriteResult wr =
            Commons.mongo.update(Tables.Message, new BasicDBObject(TMessage._Fields._ID.getFieldName(), new BasicDBObject("$in", req.getIds())), new BasicDBObject("$set", udpateMessage), false, true);
        resp.setUpdate_num(wr.getN());
        logger.trace("WriteResult: " + wr);
        return resp;
    }

    private TUserScore getTUserScore(DBObject dbobj)
    {
        if (dbobj == null)
            return null;

        TUserScore user = ThriftUtil.dbObject2ThriftObject(dbobj, TUserScore.class);
        return user;
    }

    public ListUsersResponse listTopUsers(DBObject query, int limit, String trace_tag) throws TException
    {
        ListUsersResponse res = new ListUsersResponse();
        res.setTrace_tag(trace_tag);
        BasicDBObject sort = new BasicDBObject(TUserScore._Fields.SCORE.getFieldName(), -1);
        logger.trace("query {} on user_score table, sort:{} limit:{},trace_tag:{}", query, sort, limit, trace_tag);
        DBCursor c = mongo.cursor(Tables.UserScore, query, null, sort, 0, limit);
        for (DBObject o : c)
        {
            TUserScore userScore = getTUserScore(o);
            if (userScore == null)
                continue;
            res.addToResults(userScore);
        }
        return res;
    }

    @Override
    public ListUsersResponse ListUsers(ListUsersRequest req) throws TException
    {
        List<String> fields = req.getFields();
        DBObject query = new BasicDBObject(TUserScore._Fields._ID.getFieldName(), new BasicDBObject(NE, "0"));

        if (fields.contains(ListUsersRequest._Fields.USER_ID.getFieldName()))
        {
            query.put(TUserScore._Fields._ID.getFieldName(), req.getUser_id());
        }

        if (fields.contains(ListUsersRequest._Fields.USER_IDS.getFieldName()) && (req.getUser_idsSize() > 0))
        {
            query.put(TUserScore._Fields._ID.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getUser_ids()));
        }

        if (fields.contains(ListUsersRequest._Fields.PROGRESS_ID.getFieldName()))
        {
            query.put(TUserScore._Fields.PROGRESS_ID.getFieldName(), req.getProgress_id());
        }

        if (fields.contains(ListUsersRequest._Fields.EXCLUDE_USER_IDS.getFieldName()))
        {
            query.put(TUserScore._Fields._ID.getFieldName(), new BasicDBObject(QueryOperators.NIN, req.getProgress_id()));
        }

        return listTopUsers(query, req.getLimit(), req.getTrace_tag());
    }

    /**
     * 获得精选现场排序字段
     */
    private List<TSortField> getJingXuanSortFields()
    {
        List<TSortField> sortFields = new ArrayList<TSortField>();
        // 先按置为精选时间倒序
        sortFields.add(new TSortField("ftime", -1));
        // 按现场owner正序
        sortFields.add(new TSortField("stat.owner_type", 1));
        // 按访问量倒序排
        sortFields.add(new TSortField("stat.view_num", -1));
        return sortFields;
    }

    /**
     * 选取某种排序规则下的第一篇日记
     * @param sceneId 现场id
     * @param sorts 排序方式
     */
    private TDiary getOneDiary(String sceneId, List<TSortField> sorts)
    {
        DBObject cond =
            new BasicDBObject(TDiary._Fields.STATUS.getFieldName(), ENTITY_STATUS_NORM).append(TDiary._Fields.SCENE_ID.getFieldName(), sceneId);
        DBObject dbObject = mongo.findOne(Tables.Diary, cond, null, sortField(sorts));
        return dbObject == null ? null : ThriftUtil.dbObject2ThriftObject(dbObject, TDiary.class);
    }

    /**
     * 第一篇日记
     */
    private TDiary getFirstDiary(String sceneId) throws TException
    {
        return getOneDiary(sceneId, Arrays.asList(new TSortField(TDiary._Fields.CTIME.getFieldName(), 1)));
    }

    /**
     * 最新一篇日记
     */
    private TDiary getNewestDiary(String sceneId) throws TException
    {
        return getOneDiary(sceneId, Arrays.asList(new TSortField(TDiary._Fields.PROGRESS_ID.getFieldName(), -1), new TSortField(TDiary._Fields.CTIME.getFieldName(), -1)));
    }

    /**
     * 现场筛选条件
     */
    public DBObject getSceneCond(ListSceneCondition cond)
    {
        BasicDBObject query = new BasicDBObject();

        if (cond == null)
        {
            query.put(TScene._Fields.STATUS.getFieldName(), ENTITY_STATUS_NORM);
            return query;
        }

        if (cond.getStatusSize() == 0)
            query.put(TScene._Fields.STATUS.getFieldName(), ENTITY_STATUS_NORM);

        List<String> fields = cond.getFields();
        if (fields.contains(ListSceneCondition._Fields.BUDGET_RANGE.getFieldName()) && null != cond.getBudget_range())
        {
            TRange budgeRange = cond.getBudget_range();
            // 筛选的是通过审核的清单总价
            query.put("stat.product_price.1", new BasicDBObject(GTE, budgeRange.getMini_value()).append(LTE, budgeRange.getMax_value()));

        }
        if (fields.contains(ListSceneCondition._Fields.STYLE.getFieldName()) && cond.getStyleSize() > 0)
        {
            query.put(TScene._Fields.STYLE.getFieldName(), new BasicDBObject(IN, cond.getStyle()));
        }
        if (fields.contains(ListSceneCondition._Fields.AREA_RANGE.getFieldName()) && null != cond.getArea_range())
        {
            TRange areaRange = cond.getArea_range();

            query.put(TScene._Fields.AREA.getFieldName(), new BasicDBObject(GTE, areaRange.getMini_value()).append(LTE, areaRange.getMax_value()));

        }
        if (fields.contains(ListSceneCondition._Fields.PROGRESS_IDS.getFieldName()) && cond.getProgress_idsSize() > 0)
        {
            query.put(TScene._Fields.PROGRESS_ID.getFieldName(), new BasicDBObject(IN, cond.getProgress_ids()));
        }

        if (fields.contains(ListSceneCondition._Fields.IDS.getFieldName()) && cond.getIdsSize() > 0)
        {
            query.put(TScene._Fields._ID.getFieldName(), new BasicDBObject(IN, cond.getIds()));
        }

        if (fields.contains(ListSceneCondition._Fields.OWNER_IDS.getFieldName()) && cond.getOwner_idsSize() > 0)
        {
            query.put(TScene._Fields.OWNER_ID.getFieldName(), new BasicDBObject(IN, cond.getOwner_ids()));
        }

        if (fields.contains(ListSceneCondition._Fields.CITIES.getFieldName()) && cond.getCitiesSize() > 0)
        {
            query.put(TScene._Fields.CITY_ID.getFieldName(), new BasicDBObject(IN, cond.getCities()));
        }

        if (fields.contains(ListSceneCondition._Fields.STATUS.getFieldName()) && cond.getStatusSize() > 0)
            query.put(TScene._Fields.STATUS.getFieldName(), new BasicDBObject(IN, cond.getStatus()));

        if (fields.contains(ListSceneCondition._Fields.FTIME.getFieldName()) && cond.getFtime() == -1)
            query.put(TScene._Fields.FTIME.getFieldName(), new BasicDBObject(GT, 0));
        return query;
    }

    /**
     * 查询精选现场
     */
    @Override
    public ListJingXuanSceneResponse ListJingXuanScene(ListJingXuanSceneRequest req) throws TException
    {
        String validResult = Validator.validListJingXuanScene(req);
        if (validResult != null)
            return new ListJingXuanSceneResponse(validResult, 0, null, req.getTrace_tag());

        ListSceneDiaryViewsRequest dvReq = new ListSceneDiaryViewsRequest();
        dvReq.setSorts(getJingXuanSortFields());
        dvReq.setUseFirstDiary(true);
        dvReq.setCond(req.getCond());
        dvReq.setLimit(req.getLimit());
        dvReq.setOffset(req.getOffset());
        dvReq.setShowEmptyScene(0);//不展示空日记

        ListSceneDiaryViewsResponse resp = ListSceneDiaryViews(dvReq);
        resp.setTrace_tag(req.getTrace_tag());
        return new ListJingXuanSceneResponse(resp.getRes(), resp.getTotal(), resp.getResults(), req.getTrace_tag());
    }

    /**
     * 真正要置顶显示的现场数等于已置顶的现场数和最多能被置顶的现场数的较小值
     * @return 真正需要置顶显示的现场数
     */
    public int getRealTopCount(ListNewestSceneRequest req, DBObject query)
    {
        String topTimeKey = "op_tags." + req.platform + ".top_time";

        // count = db.count({"op_tags.<platform>.top_time" : {"$gt":0}})
        DBObject cond = new BasicDBObject(topTimeKey, new BasicDBObject("$gt", 0));

        if (query != null)
            cond.putAll(query);

        int count = mongo.count(Tables.Scene, cond);

        // realTopCount = min(count, top_num)
        return Math.min(count, req.getTop_num());
    }

    /**
     * 将DBCursor对象转换成thrift对象集合
     * @param cursor 游标
     * @param clazz thrift对象的class
     * @return
     */
    public <T extends TBase<?, ?>> List<T> listCursor(DBCursor cursor, Class<T> clazz)
    {
        if (cursor == null)
            return Lists.newArrayList();
        return cursor.toArray().stream().map(dbObject -> ThriftUtil.dbObject2ThriftObject(dbObject, clazz)).collect(Collectors.toList());
    }

    /**
     * 查询置顶的现场
     * @param platform 平台
     */
    public List<TScene> queryTopScene(int platform, int offset, int limit, DBObject query)
    {
        String topTimeKey = "op_tags." + platform + ".top_time";
        // sort={"op_tags.<platform>.top_time":-1} 按置顶时间降序
        DBObject sort = sortField(Arrays.asList(new TSortField(topTimeKey, -1)));
        DBCursor cursor = mongo.cursor(Tables.Scene, query, null, sort, offset, limit);
        // 指定Mongo使用topTimeKey的索引
        // cursor.hint(new BasicDBObject(topTimeKey, -1));
        return listCursor(cursor, TScene.class);
    }

    /**
     * 查询未置顶的现场
     * @param platform 平台
     */
    public List<TScene> queryGeneralScene(int platform, int offset, int limit, DBObject query)
    {
        // 只显示非置顶的
        DBObject cond =
            new BasicDBObject("$or", Arrays.asList(new BasicDBObject("op_tags", null), new BasicDBObject("op_tags." + platform, null), new BasicDBObject("op_tags."
                + platform
                + ".top_time", 0)));

        if (query != null)
        {
            cond.putAll(query);
        }
        // sort={"stat.owner_type":1, "stat.recent_time":-1}
        // 按owner_type升序，最近更新时间升序
        DBObject sort = sortField(Arrays.asList(new TSortField("stat.owner_type", 1), new TSortField("stat.recent_time", -1)));
        DBCursor cursor = mongo.cursor(Tables.Scene, cond, null, sort, offset, limit);
        return listCursor(cursor, TScene.class);
    }

    /**
     * 查询最新的现场
     */
    @Override
    public ListNewestSceneResponse ListNewestScene(ListNewestSceneRequest req) throws TException
    {
        String validResult = Validator.validListNewestScene(req);
        if (validResult != null)
            return new ListNewestSceneResponse(validResult, 0, null, req.getTrace_tag());

        ListNewestSceneResponse resp = new ListNewestSceneResponse();
        resp.setTrace_tag(req.getTrace_tag());

        int offset = req.getOffset();
        int limit = req.getLimit();

        DBObject query = getSceneCond(req.getCond());
        //空日记的现场不展示
        query.put("stat.owner_type", new BasicDBObject(IN, Arrays.asList(SCENE_OWNER_NPJ, SCENE_OWNER_PJ)));

        //App需要的，要过滤精选
        if (null != req.getCond() && 0 == req.getCond().getFtime())
            query.put(TScene._Fields.FTIME.getFieldName(), 0);

        int n = getRealTopCount(req, query);

        List<TScene> scenes = null;

        if (offset + limit <= n)
        {
            // 整页都是显示置顶的内容, 直接显示置顶内容  top[offset : offset + limit]
            scenes = queryTopScene(req.getPlatform(), offset, limit, query);
        }
        else if (offset >= n)
        {
            // 整页都是显示非置顶的内容, 传入的offset是包含了已置顶内容的，要除去
            // general[offset - n : offset + limit - n]
            scenes = queryGeneralScene(req.getPlatform(), offset - n, limit, query);
        }
        else
        {
            // offset < N AND offset+limit > N  本页部分是置顶显示的现场，另一部分是普通的现场
            // 1，先查置顶的现场  top[offset : n]
            List<TScene> tops = queryTopScene(req.getPlatform(), offset, n - offset, query);
            // 2，查询普通的现场 general[0 : offset+limit]
            List<TScene> generals = queryGeneralScene(req.getPlatform(), 0, offset + limit - n, query);
            tops.addAll(generals);
            scenes = tops;
        }

        // 生成TSceneDiaryView
        for (TScene scene : scenes)
        {
            TSceneDiaryView sdv = new TSceneDiaryView();
            sdv.setScene(scene);
            sdv.setDiary(getNewestDiary(scene.get_id()));
            resp.addToResults(sdv);
        }

        // 查询总数
        int total = mongo.count(Tables.Scene, query);
        resp.setTotal(total);

        return resp;
    }

    /**
     * 现场详情接口
     */
    @Override
    public ListSceneDetailResponse ListSceneDetail(ListSceneDetailRequest req) throws TException
    {
        ListSceneDetailResponse resp = new ListSceneDetailResponse();
        resp.setTrace_tag(req.getTrace_tag());

        String sceneId = req.get_id();
        if (StringUtil.isEmpty(sceneId))
        {
            resp.setRes("ListSceneDetail：　SceneId is null!");
            return resp;
        }

        if (req.getStatusSize() == 0)
            req.setStatus(Arrays.asList(ENTITY_STATUS_NORM));

        List<Integer> diaryProgressIds = req.getDiary_progress_ids();
        if (null == diaryProgressIds || diaryProgressIds.size() == 0)
        {
            diaryProgressIds = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
            //            diaryProgressIds.add(0);//为监理添加新id     alpha.ren     20141212
        }

        BasicDBObject sceneDbObj = (BasicDBObject) Commons.mongo.findById(Tables.Scene, sceneId);
        if (sceneDbObj == null)
        {
            resp.setRes("ListSceneDetail：　Can~t find Scene by this sceneId!");
            return resp;
        }

        TScene scene = ThriftUtil.dbObject2ThriftObject(sceneDbObj, TScene.class);
        resp.setScene(scene);

        //======================取得响应参数的scene_comments=============================
        if (req.getDiary_only() != 1)
        {
            //查询出现场的回复 【每个现场的回复都是一个TCommentView对象，包含回复的回复、回复关联的日记对象】
            ListCommentsRequest sceneCommentReq = new ListCommentsRequest();
            sceneCommentReq.setFields(Arrays.asList("scene_id", "status", "tgt_type"));
            sceneCommentReq.setScene_id(Arrays.asList(sceneId));
            sceneCommentReq.setTgt_type(Arrays.asList(1, 2, 3));
            sceneCommentReq.setStatus(Arrays.asList(ENTITY_STATUS_NORM));
            sceneCommentReq.setSort(Arrays.asList(new TSortField().setField("ctime").setAsc(-1)));
            sceneCommentReq.setLimit(req.getScene_reply_num()); //现场详情页需要展示的全部回复数
            sceneCommentReq.setVersion(req.getVersion());
            ListCommentsResponse sceneCommentResp = ListComments(sceneCommentReq);
            List<TCommentView> sceneCommentViews = sceneCommentResp.getResults();
            resp.setScene_comments(sceneCommentViews);
        }

        resp.setTotal_comment(scene.getStat().getComment_num());
        resp.setRef_total_comment(scene.getStat().getRef_comment_num());

        //======================取得响应参数的diary_list================================
        // 查询出现场的日记，每一篇日记，对应一个TDiaryInfoView
        int offset = req.getOffset();
        // 根据需要定位的日记，计算offset值
        if (!StringUtil.isEmpty(req.getLocateDiaryId()) && req.getLimit() > 0)
        {
            DBObject tmp = mongo.findById(Tables.Diary, req.getLocateDiaryId());
            if (!sceneId.equals(tmp.get("scene_id").toString()))
            {
                resp.setRes("LocateDiary`s ScendId is not equal with ScenId in Request ");
                return resp;
            }
            BasicDBObject query = new BasicDBObject(TDiary._Fields.SCENE_ID.getFieldName(), sceneId);
            query.put(TDiary._Fields.STATUS.getFieldName(), new BasicDBObject(IN, req.getStatus()));
            query.put(TDiary._Fields.PROGRESS_ID.getFieldName(), new BasicDBObject(IN, diaryProgressIds));
            BasicDBObject orderBy = new BasicDBObject();
            List<TSortField> sort_fields = req.getSort();
            for (TSortField sf : sort_fields)
                orderBy.put(sf.getField(), sf.getAsc());
            DBCursor cursor = mongo.cursor(Tables.Diary, query, new BasicDBObject(TDiary._Fields._ID.getFieldName(), 1), orderBy, 0, 0);
            List<String> diaryIds = new ArrayList<String>();
            for (DBObject obj : cursor)
                diaryIds.add(obj.get("_id").toString());

            int locate = diaryIds.indexOf(req.getLocateDiaryId());
            if (locate != -1)
                offset = (locate / req.getLimit()) * req.getLimit();
        }

        ListDiariesRequest sceneDiariesReq = new ListDiariesRequest();
        sceneDiariesReq.setFields(Arrays.asList(ListDiariesRequest._Fields.SCENE_IDS.getFieldName(), ListDiariesRequest._Fields.STATUS.getFieldName()));
        sceneDiariesReq.setScene_ids(Arrays.asList(sceneId));
        List<String> scene_diary_fields = Lists.newArrayList();
        scene_diary_fields.addAll(sceneDiariesReq.getFields());
        if (diaryProgressIds.contains(10))
        {
            scene_diary_fields.add(ListDiariesRequest._Fields.PUBLISH_SOURCES.getFieldName());
            sceneDiariesReq.setPublish_sources(Arrays.asList(new Integer[] { constConstants.PUBLISH_DIARY_SOURCE_JIANLI }));
        }
        else
        {
            scene_diary_fields.add(ListDiariesRequest._Fields.PROGRESS_IDS.getFieldName());
            sceneDiariesReq.setProgress_ids(diaryProgressIds);
        }
        sceneDiariesReq.setFields(scene_diary_fields);
        sceneDiariesReq.setOffset(offset);
        sceneDiariesReq.setLimit(req.getLimit());
        sceneDiariesReq.setSort(req.getSort());
        sceneDiariesReq.setStatus(req.getStatus());
        sceneDiariesReq.setVersion(req.getVersion());
        ListDiariesResponse sceneDiariesResp = ListDiaries(sceneDiariesReq);
        List<TDiary> sceneDiaries = sceneDiariesResp.getResults();
        resp.setTotal_diary(sceneDiariesResp.getTotal());
        resp.setOffset_diary(offset);

        if (null == sceneDiaries || sceneDiaries.size() == 0)
        {
            resp.setDiary_list(new ArrayList<TDiaryInfoView>());
        }
        else
        {
            if (req.getDiary_only() == 1) //只返回日记信息
            {
                List<TDiaryInfoView> diaryInfoViews = new ArrayList<TDiaryInfoView>();
                for (TDiary diary : sceneDiaries)
                {
                    TDiaryInfoView view = new TDiaryInfoView();
                    view.setDiary(diary);
                    diaryInfoViews.add(view);
                }
                resp.setDiary_list(diaryInfoViews);
                return resp;
            }

            List<String> diaryIds = new ArrayList<String>();
            Set<String> productIds = new HashSet<String>();
            for (TDiary diary : sceneDiaries)
            {
                diaryIds.add(diary.get_id());
                if (diary.getProducts() != null)
                    productIds.addAll(diary.getProducts());
            }
            //查询出每篇日记的回复，取前diary_reply_num条
            ListCommentsRequest diaryCommentReq = new ListCommentsRequest();
            diaryCommentReq.setFields(Arrays.asList("diary_id", "status", "tgt_type"));
            diaryCommentReq.setTgt_type(Arrays.asList(TARGET_TYPE_DIARY));
            diaryCommentReq.setDiary_id(diaryIds);
            diaryCommentReq.setStatus(Arrays.asList(ENTITY_STATUS_NORM));
            TSortField orderByDiaryId = new TSortField("diary_id", 1);
            TSortField orderByCTime = new TSortField("ctime", -1);
            diaryCommentReq.setSort(Arrays.asList(orderByDiaryId, orderByCTime));
            diaryCommentReq.setLimit(0);
            diaryCommentReq.setVersion(req.getVersion());
            ListCommentsResponse diaryCommentResp = ListComments(diaryCommentReq);

            //key：diaryId,value：每篇日记的前diary_reply_num回复
            Map<String, List<TCommentView>> diaryCommentMap = new HashMap<String, List<TCommentView>>();

            for (TCommentView commentView : diaryCommentResp.getResults())
            {
                if (commentView.getReply_diary() == null)
                    continue;
                List<TCommentView> diaryCommentList = diaryCommentMap.get(commentView.getReply_diary().get_id());
                if (diaryCommentList == null)
                {
                    diaryCommentList = Lists.newArrayList();
                    diaryCommentMap.put(commentView.getReply_diary().get_id(), diaryCommentList);
                }
                if (diaryCommentList.size() >= req.getDiary_reply_num())
                    continue;
                diaryCommentList.add(commentView);
            }

            //每一篇日记需要List<TProduct>
            Map<String, List<TProduct>> diaryProductsMap = new HashMap<String, List<TProduct>>();
            if (productIds.size() > 0)
            {
                ListProductRequest productsReq = new ListProductRequest();
                productsReq.setFields(Arrays.asList("_ids", "status", "sort"));
                productsReq.set_ids(new ArrayList<String>(productIds));
                productsReq.setSort(Arrays.asList(new TSortField("ctime", -1)));
                productsReq.setStatus(req.getStatus());
                productsReq.setLimit(productIds.size());
                ListProductResponse productsResp = ListProduct(productsReq);
                for (TDiary diary : sceneDiaries)
                {
                    if (null == diary.getProducts() || diary.getProducts().size() == 0)
                        continue;
                    for (TProduct product : productsResp.getResults())
                    {
                        if (diary.getProducts().contains(product.get_id()))
                        {
                            List<TProduct> products = diaryProductsMap.get(diary.get_id());
                            if (null == products)
                            {
                                products = new ArrayList<TProduct>();
                                diaryProductsMap.put(diary.get_id(), products);
                            }
                            products.add(product);
                        }
                    }
                }
            }

            List<TDiaryInfoView> diaryInfoViews = new ArrayList<TDiaryInfoView>();
            for (TDiary diary : sceneDiaries)
            {
                TDiaryInfoView diaryView = new TDiaryInfoView();
                diaryView.setDiary(diary);
                diaryView.setTotal_comment(diary.getStat().getComment_num());
                diaryView.setProducts(diaryProductsMap.get(diary.get_id()) == null ? new ArrayList<TProduct>() : diaryProductsMap.get(diary.get_id()));
                diaryView.setComments(diaryCommentMap.get(diary.get_id()));
                diaryInfoViews.add(diaryView);
            }
            resp.setDiary_list(diaryInfoViews);
        }
        return resp;
    }

    public int calculateRecommendScore(RecommendSceneRequest req, TScene scene)
    {
        int score = 0;
        if (req.getHouse_type() == scene.getHouse_type())
        {
            score++;
        }

        if (req.getStyle() != null && scene.getStyle() != null)
        {
            for (int style : req.getStyle())
            {
                if (scene.getStyle().contains(style))
                {
                    score++;
                    break;
                }
            }
        }

        score += Math.abs(req.getArea() - scene.getArea()) <= 20 ? 1 : 0;

        return score;
    }

    /**
     * 获取推荐现场
     */
    @Override
    public RecommendSceneResponse RecommendScene(RecommendSceneRequest req) throws TException
    {
        String validResult = validRecommendSceneRequest(req);
        if (validResult != null)
            return new RecommendSceneResponse(validResult, 0, null, req.getTrace_tag());

        DBObject cond = new BasicDBObject("ftime", new BasicDBObject("$gt", 0));
        if (req.getExcludeIdsSize() > 0)
            cond.put(TScene._Fields._ID.getFieldName(), new BasicDBObject(NIN, req.getExcludeIds()));
        DBCursor cursor = mongo.cursor(Tables.Scene, cond, null, null, 0, 0);

        int recommends = Math.min(cursor.size(), req.getLimit());

        List<TScene> choiceScenes = listCursor(cursor, TScene.class);

        List<TScene> recommendScenes = choiceScenes.stream().sorted((s1, s2) -> {
            return calculateRecommendScore(req, s2) - calculateRecommendScore(req, s1);
        }).limit(recommends).collect(Collectors.toList());

        RecommendSceneResponse resp = new RecommendSceneResponse();

        resp.setResults(recommendScenes);
        resp.setTotal(recommends);
        return resp;
    }

    /**
     * 处理排序字段，生成排序的DBObject
     */
    private DBObject sortField(List<TSortField> sorts)
    {
        if (sorts == null || sorts.size() == 0)
            return null;
        DBObject orderby = new BasicDBObject();
        for (TSortField sort : sorts)
            orderby.put(sort.getField(), sort.getAsc());
        return orderby;
    }

    /**
     * 审核接口
     * @param tgt_type 被审核对象(1.现场 2.日记 3.商品)
     * @param tgt_id  被审核对象的id
     * @param status 审核状态
     * @param remark 驳回理由
     */
    public String verify(int tgt_type, String tgt_id, int status, String remark) throws TException
    {
        //审核的状态只能是：正常 和 审核驳回
        if (status != ENTITY_STATUS_NORM && status != ENTITY_STATUS_REJECT)
            return "Fail! Verify-Status is error!";

        DBObject cond = new BasicDBObject("_id", tgt_id);
        DBObject set = new BasicDBObject("status", status);
        if (status == ENTITY_STATUS_REJECT && !StringUtil.isEmpty(remark))
            set.put("remark", remark);
        DBObject oper = new BasicDBObject("$set", set);

        WriteResult wr = mongo.update(tables[tgt_type], cond, oper, false, false);
        if (wr == null)
            return "db fail!";

        //审核日记通过，如果现场没有封面，则选取日记中的最后一张图片作为封面
        if (tgt_type == TARGET_TYPE_DIARY && status == ENTITY_STATUS_NORM)
        {
            BasicDBObject obj = (BasicDBObject) mongo.findById(Tables.Diary, tgt_id);
            TDiary diary = ThriftUtil.dbObject2ThriftObject(obj, TDiary.class);
            if (diary.getImagesSize() > 0)
            {
                DBObject scene_db = mongo.findById(Tables.Scene, diary.getScene_id());
                if (null != scene_db)
                {
                    BasicDBObject coverImgObj = (BasicDBObject) scene_db.get(TScene._Fields.COVER_IMAGE.getFieldName());
                    if (coverImgObj == null || StringUtil.isEmpty(coverImgObj.getString("id")))
                    {
                        List<TImage> images = diary.getImages();
                        TImage image = images.get(images.size() - 1);
                        coverImgObj = new BasicDBObject("cover_image", new BasicDBObject("id", image.getId()).append("spec", image.getSpec()));
                        mongo.updateField(Tables.Scene, diary.getScene_id(), coverImgObj);
                    }
                }
            }
        }

        return "succ";
    }

    /**
     * 审核接口
     */
    @Override
    public VerifyResponse verify(VerifyRequest req) throws TException
    {
        String validResult = validVerifyRequest(req);
        if (validResult != null)
            return new VerifyResponse(validResult, 0, null, 0, req.getTrace_tag());

        String res = verify(req.getEntity_type(), req.getEntity_id(), req.getStatus(), req.getRemark());
        VerifyResponse resp = new VerifyResponse();
        resp.setTrace_tag(req.getTrace_tag());

        resp.setRes(res);

        if ("succ".equals(res))
            StatWriter.sendMsg(req);
        return resp;
    }

    @Override
    public CreateResponse AddDiaryProduct(AddDiaryProductRequest req) throws TException
    {
        appendProductToDiary(req.getDiary_id(), req.getProduct_id());
        return new CreateResponse("succ", null, req.getTrace_tag());
    }

    @Override
    public DeleteResponse RemoveDiaryProduct(RemoveDiaryProductRequest req) throws TException
    {
        DBObject cond = new BasicDBObject("_id", req.getDiary_id());
        DBObject oper = new BasicDBObject("$pull", new BasicDBObject("products", req.getProduct_id()));
        WriteResult wr = mongo.update(Tables.Diary, cond, oper, false, false);
        if (wr != null)
            return new DeleteResponse("succ", wr.getN(), req.getTrace_tag());
        return new DeleteResponse("db failed!", 0, req.getTrace_tag());
    }

    /**
     * 查询所有标签（大阶段&小阶段）的日记统计
     */
    @Override
    public ListAllCategoryStatResponse ListAllCategoryStat(ListAllCategoryStatRequest req) throws TException
    {
        ListAllCategoryStatResponse resp = new ListAllCategoryStatResponse();

        int showTag = req.getShow_tag();
        if (showTag != 0 && showTag != 1) //0 显示；1 不显示
            showTag = 0;

        Map<String, List<TStType>> configMap = getConfigMap(req.getVersion());
        if (configMap == null || configMap.size() == 0)
        {
            resp.setRes("Get config error !");
            return resp;
        }

        Map<Integer, TProgressDiaryCount> progressDiaryCountMap = new HashMap<Integer, TProgressDiaryCount>();
        Map<Integer, TTagDiaryCount> tagDiaryCountMap = new HashMap<Integer, TTagDiaryCount>();
        if (showTag == 0)
        {
            List<TStType> types = configMap.get(CONF_PROG_TYPE_2);
            for (TStType type : types)
            {
                for (String id : type.getId_map().keySet())
                {
                    TTagDiaryCount tag = new TTagDiaryCount();
                    tag.set_id(id);
                    tag.setName(type.getId_map().get(id));
                    tag.setParent_id(type.getParent_type_id());
                    tag.setDiary_count(0);
                    tagDiaryCountMap.put(Integer.parseInt(id), tag);
                }
            }
        }

        TStType type = configMap.get(CONF_PROG_TYPE_1).get(0);
        for (String id : type.getId_map().keySet())
        {
            TProgressDiaryCount progress = new TProgressDiaryCount();
            progress.set_id(id);
            progress.setName(type.getId_map().get(id));
            progress.setDiary_count(0); //日记数量初始值设置为0，后面统计到的再更新
            progress.setTag_diary_counts(new ArrayList<TTagDiaryCount>());
            progressDiaryCountMap.put(Integer.parseInt(id), progress);
        }

        List<Integer> status = req.getStatus();
        if (null == status || status.size() == 0)
            status = Arrays.asList(ENTITY_STATUS_NORM);

        BasicDBObject condition = null;
        if (status.contains(ENTITY_STATUS_DRAFT)) //草稿都不计入统计
        {
            status.remove(new Integer(ENTITY_STATUS_DRAFT));
            if (status.size() == 0)
                status.add(ENTITY_STATUS_NORM);
        }

        if (status.size() == 1)
            condition = new BasicDBObject("status", status.get(0));
        else
            condition = new BasicDBObject("status", new BasicDBObject(IN, status));

        //限制tag_id，使之不为null       alpha.ren   2014-12-25                
        condition.put(TDiary._Fields.TAG_ID.getFieldName(), new BasicDBObject(NE, null));

        if (req.getScene_idsSize() > 0)
        {
            if (req.getScene_idsSize() == 1)
                condition.put("scene_id", req.getScene_ids().get(0));
            else
                condition.put("scene_id", new BasicDBObject(IN, req.getScene_ids()));
        }

        if (showTag == 0)
        {
            int tmp_51 = 0; //碎碎念，新版本统计时，需要统计旧tag的日记数
            int tmp_50 = 0; //产品选购
            List<DBObject> pipeline = new ArrayList<DBObject>();
            pipeline.add(new BasicDBObject("$match", condition));
            pipeline.add(new BasicDBObject("$group", new BasicDBObject("_id", "$tag_id").append("count", new BasicDBObject("$sum", 1))));
            AggregationOutput output = Commons.mongo.getDb().getCollection("diary").aggregate(pipeline);
            for (Object obj : output.results())
            {
                BasicDBObject result = (BasicDBObject) obj;
                int tag_id = result.getInt("_id");
                TTagDiaryCount tagDiaryCount = tagDiaryCountMap.get(tag_id);
                int count = result.getInt("count");
                if (null == tagDiaryCount)
                {
                    if (req.getVersion() == VERSION_2_5)
                    {
                        if (oldTagsProdSele.contains(tag_id))
                            tmp_50 += count;
                        else if (oldTagsSsn.contains(tag_id))
                            tmp_51 += count;
                    }
                    continue;
                }
                tagDiaryCount.setDiary_count(count);
            }

            for (TTagDiaryCount tag : tagDiaryCountMap.values())
            {
                if (req.getVersion() == VERSION_2_5)
                {
                    if ("50".equals(tag.get_id()))
                        tag.setDiary_count(tag.getDiary_count() + tmp_50);
                    else if ("51".equals(tag.get_id()))
                        tag.setDiary_count(tag.getDiary_count() + tmp_51);
                }
            }
        }

        List<DBObject> pipeline = new ArrayList<DBObject>();
        pipeline.add(new BasicDBObject("$match", condition));
        pipeline.add(new BasicDBObject("$group", new BasicDBObject("_id", "$progress_id").append("count", new BasicDBObject("$sum", 1))));
        AggregationOutput output = Commons.mongo.getDb().getCollection("diary").aggregate(pipeline);
        for (DBObject obj : output.results())
        {
            BasicDBObject result = (BasicDBObject) obj;
            TProgressDiaryCount progressDiaryCount = progressDiaryCountMap.get(result.getInt("_id"));
            if (null == progressDiaryCount)
                continue;
            progressDiaryCount.setDiary_count(result.getInt("count"));
            if (result.getInt("_id") == 9 && req.getVersion() == VERSION_2_5)
            {
                TTagDiaryCount tag51 = tagDiaryCountMap.get(51);
                int count51 = tag51 == null ? 0 : tag51.getDiary_count();
                TTagDiaryCount tag50 = tagDiaryCountMap.get(50);
                int count50 = tag50 == null ? 0 : tag50.getDiary_count();
                progressDiaryCount.setDiary_count(progressDiaryCount.getDiary_count() + count51 + count50);
            }
            /*if (showTag == 0)
                for (TTagDiaryCount sub : tagDiaryCountMap.values())
                    if (progressDiaryCount.get_id().equals(sub.getParent_id()))
                        progressDiaryCount.getTag_diary_counts().add(sub);*/
        }

        //将小阶段关联到大阶段下面（现在大阶段和小阶段已经不强关联，暂时还使用之前的结构，不进行大的修改）
        if (showTag == 0)
            for (TProgressDiaryCount progressDiaryCount : progressDiaryCountMap.values())
                for (TTagDiaryCount sub : tagDiaryCountMap.values())
                    if (progressDiaryCount.get_id().equals(sub.getParent_id()))
                        progressDiaryCount.getTag_diary_counts().add(sub);

        List<TProgressDiaryCount> values = new ArrayList<TProgressDiaryCount>(progressDiaryCountMap.values());

        //添加监理筛选标签，改标签下显示所有的监理日志   1:显示监理统计;  默认为不显示        alpha.ren   2014.12.11
        if (req.getScene_idsSize() == 1 && req.getWith_jianli() > 0)
        {
            ListScenesRequest scene_req = new ListScenesRequest();
            scene_req.setFields(Arrays.asList(new String[] { ListScenesRequest._Fields._IDS.getFieldName() }));
            scene_req.set_ids(req.getScene_ids());
            ListScenesResponse scene_res = this.ListScenes(scene_req);

            int jianli_count = 0;
            if (scene_res.getResultsSize() > 0)
            {
                TSceneStat scene_stat = scene_res.getResults().get(0).getStat();
                if (null != scene_stat)
                {
                    Map<String, Integer> owner_counts = scene_stat.getOwner_counts();
                    if (null != owner_counts)
                    {
                        if (null != owner_counts.get("j"))
                            jianli_count = owner_counts.get("j");
                    }
                }
            }
            //_id与progress无关，所以这里设为0
            values.add(new TProgressDiaryCount("10", "监理日记", jianli_count, null));
        }

        resp.setResults(values);
        return resp;
    }

    private Map<String, List<TStType>> getConfigMap(double version) throws TException
    {
        if (version == VERSION_2_5 && configMap_v25.get(CONF_PROG_TYPE_1) != null && configMap_v25.get(CONF_PROG_TYPE_2) != null)
            return configMap_v25;

        if (version < VERSION_2_5 && configMap_v20.get(CONF_PROG_TYPE_1) != null && configMap_v20.get(CONF_PROG_TYPE_2) != null)
            return configMap_v20;
        //获取大阶段、小阶段配置
        GetAllStConfigIdMapRequest getConfigReq = new GetAllStConfigIdMapRequest();
        getConfigReq.setVersion(version);
        GetAllStConfigIdMapResponse configResp = GetAllStConfigIdMap(getConfigReq);
        if (configResp.getSt_typesSize() == 0)
        {
            return null;
        }
        List<TStType> stTypes = configResp.getSt_types();
        Map<String, List<TStType>> tmpConfig = new ConcurrentHashMap<String, List<TStType>>();
        for (TStType type : stTypes)
        {
            if (CONF_PROG_TYPE_1.equals(type.getType_name()))
            {
                List<TStType> list = tmpConfig.get(CONF_PROG_TYPE_1);
                if (list == null)
                {
                    list = new ArrayList<TStType>();
                    tmpConfig.put(CONF_PROG_TYPE_1, list);
                }
                list.add(type);
            }

            if (CONF_PROG_TYPE_2.equals(type.getType_name()))
            {
                List<TStType> list = tmpConfig.get(CONF_PROG_TYPE_2);
                if (list == null)
                {
                    list = new ArrayList<TStType>();
                    tmpConfig.put(CONF_PROG_TYPE_2, list);
                }
                list.add(type);
            }
        }
        if (version == VERSION_2_5)
        {
            configMap_v25.clear();
            configMap_v25.putAll(tmpConfig);
            return configMap_v25;
        }
        else if (version < VERSION_2_5)
        {
            configMap_v20.clear();
            configMap_v20.putAll(tmpConfig);
            return configMap_v20;
        }
        return null;
    }

    @Override
    public GetStConfigSubIdMapResponse GetStConfigSubIdMap(GetStConfigSubIdMapRequest req) throws TException
    {
        String validResult = Validator.validGetStConfigSubIdMapRequest(req);
        if (validResult != null)
        {
            return new GetStConfigSubIdMapResponse(validResult, null, req.getTrace_tag());
        }

        String type_name = req.getType_name();
        String parent_id = req.getParent_id();

        DBObject st_db = null;
        TStType st_obj;
        GetStConfigSubIdMapResponse st_response = new GetStConfigSubIdMapResponse();
        if (StringUtil.isEmpty(parent_id))
            st_db =
                Commons.mongo.findOne(Tables.StaticConfigValue, new BasicDBObject(TStType._Fields.TYPE_NAME.getFieldName(), type_name), null, null);
        else
        {
            st_db =
                Commons.mongo.findOne(Tables.StaticConfigValue, new BasicDBObject().append(TStType._Fields.TYPE_NAME.getFieldName(), type_name).append(TStType._Fields.PARENT_TYPE_ID.getFieldName(), parent_id), null, null);
        }
        if (null != st_db)
        {
            st_obj = ThriftUtil.dbObject2ThriftObject(st_db, TStType.class);
            st_response.setIds_map(st_obj.getId_map());
        }
        return st_response;
    }

    @Override
    public GetAllStConfigIdMapResponse GetAllStConfigIdMap(GetAllStConfigIdMapRequest req) throws TException
    {

        GetAllStConfigIdMapResponse res = new GetAllStConfigIdMapResponse();

        String st_config_table = Tables.StaticConfigOld;
        if (req.getVersion() > 2)
            st_config_table = Tables.StaticConfigValue;

        DBCursor cursor = Commons.mongo.cursor(st_config_table, new BasicDBObject(), null, null, 0, 0);
        if (null == cursor)
        {
            res.setRes("db fail!");
            return res;
        }

        List<TStType> st_types = new ArrayList<TStType>();
        while (cursor.hasNext())
        {
            DBObject st_db = cursor.next();
            if (null != st_db)
            {
                TStType st_type = ThriftUtil.dbObject2ThriftObject(st_db, TStType.class);
                Map<String, String> id_map = st_type.getId_map();
                Map<String, String> sorted_id_map = new TreeMap<String, String>(new Comparator<String>()
                {
                    @Override
                    public int compare(String o1, String o2)
                    {
                        return Integer.parseInt(o1.toString()) - Integer.parseInt(o2.toString());
                    }
                });

                for (String key : id_map.keySet())
                {
                    sorted_id_map.put(key, id_map.get(key));
                }
                st_type.setId_map(sorted_id_map);
                st_types.add(st_type);
            }
        }
        res.setSt_types(st_types);
        return res;

    }

    @Override
    public ListSceneDiaryViewsResponse ListSceneDiaryViews(ListSceneDiaryViewsRequest req) throws TException
    {
        String validResult = Validator.validListSceneDiaryViews(req);
        if (validResult != null)
            return new ListSceneDiaryViewsResponse(validResult, 0, null, req.getTrace_tag());

        List<Integer> statusList = null;
        if (req.getCond() == null || req.getCond().getStatusSize() == 0)
            statusList = Arrays.asList(ENTITY_STATUS_NORM);
        else
            statusList = req.getCond().getStatus();

        // 排序方式
        DBObject sort = sortField(req.getSorts());

        // 筛选条件
        DBObject query = getSceneCond(req.getCond());

        if (req.getShowEmptyScene() != 0 && req.getShowEmptyScene() != 1)
            req.setShowEmptyScene(0);

        if (req.getShowEmptyScene() == 0)//去掉没有日记的现场
            query.put("stat.owner_type", new BasicDBObject(IN, Arrays.asList(SCENE_OWNER_NPJ, SCENE_OWNER_PJ)));

        DBCursor cursor = mongo.cursor(Tables.Scene, query, null, sort, req.getOffset(), req.getLimit());
        logger.debug("trace_tag:{} query:{}", req.getTrace_tag(), query);

        ListSceneDiaryViewsResponse resp = new ListSceneDiaryViewsResponse();
        resp.setTrace_tag(req.getTrace_tag());
        // 设置总数
        resp.setTotal(cursor.count());

        List<TSceneDiaryView> sdvs = new ArrayList<TSceneDiaryView>();
        while (cursor.hasNext())
        {
            DBObject dbObject = cursor.next();
            TScene scene = ThriftUtil.dbObject2ThriftObject(dbObject, TScene.class);

            TSceneDiaryView sdv = new TSceneDiaryView();
            sdv.setScene(scene);

            DBObject cond =
                new BasicDBObject(TDiary._Fields.STATUS.getFieldName(), new BasicDBObject(IN, statusList)).append(TDiary._Fields.SCENE_ID.getFieldName(), scene.get_id());
            if (req.isUseFirstDiary())
            {
                DBObject obj = mongo.findOne(Tables.Diary, cond, null, new BasicDBObject("ctime", 1));
                sdv.setDiary(dbObject == null ? null : ThriftUtil.dbObject2ThriftObject(obj, TDiary.class));
            }
            else
            {
                DBObject obj = mongo.findOne(Tables.Diary, cond, null, new BasicDBObject("progress_id", -1).append("ctime", -1));
                sdv.setDiary(obj == null ? null : ThriftUtil.dbObject2ThriftObject(obj, TDiary.class));
            }

            sdvs.add(sdv);
        }
        resp.setResults(sdvs);

        return resp;
    }

    @Override
    public JudgeProductUsedResponse JudgeProductUsed(JudgeProductUsedRequest req) throws TException
    {
        Map<String, Boolean> used = new HashMap<String, Boolean>();
        for (int i = 0; i < req.getProduct_idsSize(); i++)
        {
            String productId = req.getProduct_ids().get(i);
            DBObject cond = new BasicDBObject(TDiary._Fields.PRODUCTS.getFieldName(), new BasicDBObject(IN, Arrays.asList(productId)));
            boolean exist = mongo.exist(Tables.Diary, cond);
            used.put(productId, exist);
        }
        JudgeProductUsedResponse resp = new JudgeProductUsedResponse();
        resp.setUsed(used);
        return resp;
    }

    @Override
    public ListScenesResponse ListSceneRaw(ListSceneRawRequest req) throws TException
    {
        String filter_json = req.getFilter_json();

        ListScenesResponse res = new ListScenesResponse();
        res.setTrace_tag(req.getTrace_tag());

        BasicDBObject query = new BasicDBObject();

        if (!StringUtil.isEmpty(filter_json))
        {
            BasicDBObject parsed = (BasicDBObject) JSON.parse(filter_json);

            query = Validator.validateSceneFilter(parsed);
        }

        if (query.isEmpty())
        {
            logger.error("invalue filter_json!");
            res.setRes("invalue filter_json!");
        }
        else
        {

            BasicDBObject sort = new BasicDBObject();

            if (req.getSort() != null)
            {
                List<TSortField> sorts = req.getSort();

                for (TSortField sf : sorts)
                    sort.put(sf.getField(), sf.getAsc());
            }

            int offset = req.getOffset();

            int limit = req.getLimit();

            logger.trace("search scene table, trace_tag:{} query:{}, sort:{}, offset:{}, limit:{}", req.getTrace_tag(), query, sort, offset, limit);

            DBCursor c = null;
            if (query.containsField(TScene._Fields.CASE_ID.getFieldName()))
                c = mongo.cursor(Tables.Scene, query, null, sort, offset, limit).hint(new BasicDBObject(TScene._Fields.CASE_ID.getFieldName(), 1));
            else if (query.containsField(TScene._Fields.COM_ID.getFieldName()))
                c = mongo.cursor(Tables.Scene, query, null, sort, offset, limit).hint(new BasicDBObject(TScene._Fields.COM_ID.getFieldName(), 1));
            else
                c = mongo.cursor(Tables.Scene, query, null, sort, offset, limit);

            DBObject cur_explain = c.explain();
            Object time_spend = cur_explain.get("millis");
            logger.trace("trace_tag:{},time_spend:{}", req.getTrace_tag(), time_spend);
            if (null != time_spend && StringUtil.isNumeric(time_spend.toString()))
            {
                long time_long = Long.parseLong(time_spend.toString());
                if (time_long > 100)
                {
                    logger.warn("trace_tag:{} 超过100ms explain:{}", req.getTrace_tag(), cur_explain);
                }
            }

            int total = c.count();

            res.setTotal(total);

            res.setOffset(offset);

            res.setLimit(limit);

            for (DBObject o : c)
            {
                TScene scene = ThriftUtil.dbObject2ThriftObject(o, TScene.class);
                res.addToResults(scene);
            }
        }

        traceThrift(res);
        return res;
    }

    @Override
    public UpdateResponse UpdateRawScene(UpdateSceneRawRequest req) throws TException
    {
        return new UpdateResponse();
    }

    @Override
    public UpdateResponse UpdateUser(UpdateUserRequest req) throws TException
    {
        UpdateResponse res = new UpdateResponse();
        String validate_result = Validator.validListUpdateUserRequest(req);

        res.setTrace_tag(req.getTrace_tag());
        if (StringUtil.isEmpty(validate_result))
        {
            BasicDBObject update_user_db = getUpdateDBObj(req.getFields(), req.getUser());
            update_user_db.remove(TUserScore._Fields._ID.getFieldName());

            WriteResult wr =
                Commons.mongo.update(Tables.UserScore, new BasicDBObject(TUserScore._Fields._ID.getFieldName(), req.get_id()), new BasicDBObject("$set", update_user_db), true, false);

            logger.trace("WriteResult: " + wr);
        }
        else
        {
            res.setRes(validate_result);
        }
        this.traceThrift(res);
        return res;
    }

    @Override
    public GetStOrderListResponse GetStOrderList(GetStOrderListRequest req) throws TException
    {
        String type_name = req.getType_name();
        int parent_id = req.getParent_id();

        GetStOrderListResponse res = new GetStOrderListResponse();

        GetStConfigSubIdMapRequest conf_req = new GetStConfigSubIdMapRequest();
        conf_req.setType_name(type_name);
        conf_req.setParent_id("" + parent_id);
        GetStConfigSubIdMapResponse conf_res = this.GetStConfigSubIdMap(conf_req);
        if (conf_res.getRes().equals("succ"))
        {
            Map<String, String> conf_map = conf_res.getIds_map();
            List<TStElem> elems = new ArrayList<TStElem>();
            if (null != conf_map)
            {
                for (String sub_id : conf_map.keySet())
                {
                    elems.add(new TStElem(Integer.parseInt(sub_id), conf_map.get(sub_id)));
                }
            }
            TStOrderList order_list = new TStOrderList(parent_id, type_name, elems);
            res.setOrder_list(order_list);
        }
        else
        {
            res.setRes(conf_res.getRes());
        }
        return res;
    }

    /**
     * 这次先不做
     */
    @Override
    public ListProgressImagesResponse ListProgressImages(ListProgressImagesRequest req) throws TException
    {
        return new ListProgressImagesResponse();
    }

    @Override
    public ListStatisticResponse ListStatistic(ListStatisticRequest req) throws TException
    {
        String table = tables[req.getTgt_type()];
        DBObject cond = new BasicDBObject("ctime", new BasicDBObject("$gte", req.getFrom_time()).append("$lte", req.getTo_time()));
        if (req.getExclude_ids() != null && !req.getExclude_ids().isEmpty())
        {
            if (req.getTgt_type() == constConstants.TARGET_TYPE_COMMENT)
            {
                cond.put("user_id", new BasicDBObject("$nin", req.getExclude_ids()));
            }
            else
            {
                cond.put("owner_id", new BasicDBObject("$nin", req.getExclude_ids()));
            }
        }
        int count = mongo.count(table, cond);
        ListStatisticResponse resp = new ListStatisticResponse();
        resp.setCount(count);
        return resp;
    }

    /**
     * 轮单接口，根据装修公司id查询装修公司的所有现场
     */
    @Override
    public ListCompanyAllScenesResponse ListCompanyAllScenes(ListCompanyAllScenesRequest req) throws TException
    {
        ListCompanyAllScenesResponse res = new ListCompanyAllScenesResponse();
        int com_id = req.getCom_id();
        BasicDBObject query = new BasicDBObject("com_id", com_id).append("status", ENTITY_STATUS_NORM);
        BasicDBObject fields = new BasicDBObject("_id", 1);
        fields.put("com_id", 1);
        fields.put("comm_id", 1);
        fields.put("style", 1);
        fields.put("town_id", 1);
        fields.put("city_id", 1);

        DBCursor c = mongo.cursor(Tables.Scene, query, fields, null, 0, 0);

        res.setScenes(new ArrayList<TSceneLundan>());
        for (DBObject o : c)
        {
            TSceneLundan scene = ThriftUtil.dbObject2ThriftObject(o, TSceneLundan.class);
            res.addToScenes(scene);
        }

        res.setTotal(c.count());
        return res;
    }

    /**
     * 找我家接口：根据comm_id查询符合条件的现场
     */
    @Override
    public ListCommunityResponse ListCommunity(ListCommunityRequest req) throws TException
    {
        ListCommunityResponse resp = new ListCommunityResponse();
        resp.setTrace_tag(req.getTrace_tag());

        List<String> fields = req.getFields();

        BasicDBObject query = new BasicDBObject();

        query.put(TScene._Fields.COMM_ID.getFieldName(), new BasicDBObject(QueryOperators.GT, 0));

        if (fields.contains(ListCommunityRequest._Fields.COMM_IDS.getFieldName()) && req.getComm_idsSize() > 0)
            query.put(TScene._Fields.COMM_ID.getFieldName(), new BasicDBObject(IN, req.getComm_ids()));

        if (fields.contains(ListCommunityRequest._Fields.COMM_NAME.getFieldName()))
        {
            if (!StringUtil.isEmpty(req.getComm_name()))
            {
                Pattern pattern = Pattern.compile("^.*" + req.getComm_name() + ".*$", Pattern.CASE_INSENSITIVE);
                query.put(TScene._Fields.COMM_NAME.getFieldName(), pattern);
            }
        }

        if (fields.contains(ListCommunityRequest._Fields.AREA_RANGE.getFieldName()))
        {
            if (req.getArea_range() != null)
            {
                TRange area_range = req.getArea_range();
                query.put(TScene._Fields.AREA.getFieldName(), new BasicDBObject(GTE, area_range.getMini_value()).append(LTE, area_range.getMax_value()));
            }
        }

        if (fields.contains(ListCommunityRequest._Fields.BUDGET_RANGE.getFieldName()))
        {
            if (req.getBudget_range() != null)
            {
                TRange budget_range = req.getBudget_range();
                query.put(TScene._Fields.BUDGET.getFieldName(), new BasicDBObject(GTE, budget_range.getMini_value()).append(LTE, budget_range.getMax_value()));
            }
        }

        if (fields.contains(ListCommunityRequest._Fields.CITY_IDS.getFieldName()))
        {
            if (req.getCity_idsSize() > 0)
            {
                if (req.getCity_idsSize() == 1)
                    query.put(TScene._Fields.CITY_ID.getFieldName(), req.getCity_ids().get(0));
                else
                    query.put(TScene._Fields.CITY_ID.getFieldName(), new BasicDBObject(IN, req.getCity_ids()));
            }
        }

        if (fields.contains(ListCommunityRequest._Fields.HOUSE_TYPES.getFieldName()))
        {
            if (req.getHouse_typesSize() > 0)
            {
                if (req.getHouse_typesSize() == 1)
                    query.put(TScene._Fields.HOUSE_TYPE.getFieldName(), req.getHouse_types().get(0));
                else
                    query.put(TScene._Fields.HOUSE_TYPE.getFieldName(), new BasicDBObject(IN, req.getHouse_types()));
            }
        }

        if (fields.contains(ListCommunityRequest._Fields.TOWN_IDS.getFieldName()))
        {
            if (req.getTown_idsSize() > 0)
            {
                if (req.getTown_idsSize() == 1)
                    query.put(TScene._Fields.TOWN_ID.getFieldName(), req.getTown_ids().get(0));
                else
                    query.put(TScene._Fields.TOWN_ID.getFieldName(), new BasicDBObject(IN, req.getTown_ids()));
            }
        }

        if (fields.contains(ListCommunityRequest._Fields.STYLE.getFieldName()))
        {
            if (req.getStyleSize() > 0)
            {
                List<DBObject> or = new ArrayList<DBObject>();
                for (Integer style : req.getStyle())
                    or.add(new BasicDBObject(ListScenesRequest._Fields.STYLE.getFieldName(), style));
                query.put(QueryOperators.OR, or);
            }
        }

        if (fields.contains(ListCommunityRequest._Fields.HAS_COM_ID.getFieldName()))
        {
            if (req.getHas_com_id() == 1)
                query.put(TScene._Fields.COM_ID.getFieldName(), new BasicDBObject(QueryOperators.GT, 0));
        }

        if (fields.contains(ListCommunityRequest._Fields.HAS_COVER_IMG_C.getFieldName()))
        {
            if (req.getHas_cover_img_c() == 1)
                query.put(TScene._Fields.COVER_IMAGE_C.getFieldName(), new BasicDBObject(QueryOperators.NE, null));
        }

        if (fields.contains(ListCommunityRequest._Fields.GONGDIJDS.getFieldName()))
        {
            if (req.getGongdijdsSize() > 0)
                query.put(TScene._Fields.GONGDIJD.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getGongdijds()));
        }

        if (fields.contains(ListCommunityRequest._Fields.STATUS.getFieldName()))
        {
            if (req.getStatusSize() > 0)
                query.put(TScene._Fields.STATUS.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getStatus()));
        }

        if (fields.contains(ListCommunityRequest._Fields.PUBLISH_SOURCES.getFieldName()))
        {
            if (req.getPublish_sourcesSize() > 0)
                query.put(TScene._Fields.PUBLISH_SOURCE.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getPublish_sources()));
        }

        if (fields.contains(ListCommunityRequest._Fields.UTIME_RANGE.getFieldName()))
        {
            TRange utime_range = req.getUtime_range();
            query.put(TScene._Fields.UTIME.getFieldName(), new BasicDBObject(GTE, utime_range.getMini_value()).append(LTE, utime_range.getMax_value()));
        }

        if (fields.contains(ListCommunityRequest._Fields.HAS_CITY_ID.getFieldName()))
        {
            query.put(TScene._Fields.CITY_ID.getFieldName(), new BasicDBObject(QueryOperators.GT, 0));
        }

        logger.debug("search scene table for community,trace_tag: {}, query: {}", req.getTrace_tag(), query);

        Map<Integer, List<TScene>> results = new HashMap<Integer, List<TScene>>();
        Map<Integer, Integer> results_counts = new HashMap<Integer, Integer>();
        resp.setResults(results);
        resp.setResults_counts(results_counts);

        BasicDBObject orderBy = new BasicDBObject();
        List<TSortField> sort_fields = req.getScene_sort();
        for (TSortField sort : sort_fields)
        {
            orderBy.put(sort.getField(), sort.getAsc());
        }

        // create our pipeline operations, first with the $match
        logger.debug("match:{}", query);
        DBObject match = new BasicDBObject("$match", query);

        // the $group operation
        DBObject groupFields = new BasicDBObject("_id", "$" + TScene._Fields.COMM_ID.getFieldName());
        groupFields.put("maxTime", new BasicDBObject("$max", "$" + TScene._Fields.CTIME.getFieldName()));
        DBObject group = new BasicDBObject("$group", groupFields);

        // the $sort operation
        DBObject sort = new BasicDBObject("$sort", new BasicDBObject("maxTime", -1));

        //        // the $skip operation
        //        DBObject skip = new BasicDBObject("$skip", req.getOffset());
        //
        //        // the $limit operation
        //        DBObject limit = new BasicDBObject("$limit", req.getLimit());

        // run aggregation

        AggregationOutput output = mongo.aggregate(Tables.Scene, match, group, sort);
        int i = 0;
        for (DBObject comm_db : output.results())
        {
            if (i >= req.getOffset() && i < (req.getOffset() + req.getLimit()))
            {
                int comm_id = (Integer) comm_db.get("_id");
                results.put(comm_id, Lists.newArrayList());
                results_counts.put(comm_id, 0);
            }
            i++;
        }
        resp.setTotal(i);

        //包含现场
        if (req.getWith_scene() > 0)
        {
            BasicDBObject origin_scene_query = new BasicDBObject();

            origin_scene_query.putAll(query.toMap());

            if (fields.contains(ListCommunityRequest._Fields.SCENE_STATUS.getFieldName()))
            {
                if (req.getScene_statusSize() > 0)
                {
                    origin_scene_query.put(TScene._Fields.STATUS.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getScene_status()));
                }
            }

            BasicDBObject scene_order = (BasicDBObject) orderBy.clone();

            for (Integer comm_id : results.keySet())
            {
                BasicDBObject scene_query = (BasicDBObject) origin_scene_query.clone();
                scene_query.put(TScene._Fields.COMM_ID.getFieldName(), comm_id);
                DBCursor scene_cur = mongo.cursor(Tables.Scene, scene_query, null, scene_order, 0, req.getScene_limit());
                List<TScene> scenes = results.get(comm_id);
                results_counts.put(comm_id, scene_cur.count());
                while (scene_cur.hasNext())
                {
                    BasicDBObject scene_db = (BasicDBObject) scene_cur.next();
                    if (null != scene_db)
                    {
                        scenes.add(ThriftUtil.dbObject2ThriftObject(scene_db, TScene.class));
                    }
                }
            }
        }

        return resp;
    }

    @Override
    public ListCompaniesResponse ListCompanies(ListCompaniesRequest req) throws TException
    {
        ListCompaniesResponse resp = new ListCompaniesResponse();
        resp.setTrace_tag(req.getTrace_tag());

        List<String> fields = new ArrayList<String>();
        if (req.getFieldsSize() > 0)
            fields = req.getFields();

        /* query for company */
        DBObject query = new BasicDBObject();
        query.put(TScene._Fields.COM_ID.getFieldName(), new BasicDBObject(GT, 0));

        if (fields.contains(ListCompaniesRequest._Fields.COM_IDS.getFieldName()))
        {
            if (req.getCom_idsSize() > 0)
                query.put(TScene._Fields.COM_ID.getFieldName(), new BasicDBObject(IN, req.getCom_ids()));
        }

        if (fields.contains(ListCompaniesRequest._Fields.CITY_IDS.getFieldName()))
        {
            if (req.getCity_idsSize() > 0)
                query.put(TScene._Fields.CITY_ID.getFieldName(), new BasicDBObject(IN, req.getCity_ids()));
        }

        if (fields.contains(ListCompaniesRequest._Fields.COMM_IDS.getFieldName()))
        {
            if (req.getComm_idsSize() > 0)
                query.put(TScene._Fields.COMM_ID.getFieldName(), new BasicDBObject(IN, req.getComm_ids()));
        }

        if (fields.contains(ListCompaniesRequest._Fields.PUBLISH_SOURCES.getFieldName()))
        {
            if (req.getPublish_sourcesSize() > 0)
                query.put(TScene._Fields.PUBLISH_SOURCE.getFieldName(), new BasicDBObject(IN, req.getPublish_sources()));
        }

        if (fields.contains(ListCompaniesRequest._Fields.HAS_COVER_IMG_C.getFieldName()) && req.getHasCoverImg_c() == 1)
            query.put(TScene._Fields.COVER_IMAGE_C.getFieldName(), new BasicDBObject(NE, null));

        if (fields.contains(ListCompaniesRequest._Fields.GONGDIJDS.getFieldName()) && req.getGongdijdsSize() > 0)
            query.put(TScene._Fields.GONGDIJD.getFieldName(), new BasicDBObject(IN, req.getGongdijds()));

        if (fields.contains(ListCompaniesRequest._Fields.STATUS.getFieldName()) && req.getStatusSize() > 0)
            query.put(TScene._Fields.STATUS.getFieldName(), new BasicDBObject(IN, req.getStatus()));

        logger.debug("search scene table for companies, trace_tag:{} query:{}", req.getTrace_tag(), query);

        Map<Integer, List<TScene>> results = new HashMap<Integer, List<TScene>>();
        Map<Integer, Integer> results_counts = new HashMap<Integer, Integer>();
        resp.setResults(results);
        resp.setResults_counts(results_counts);

        /* sort for scene*/
        BasicDBObject orderBy = new BasicDBObject();
        List<TSortField> sort_fields = req.getSort();
        if (null == sort_fields)
        {
            sort_fields = new ArrayList<TSortField>();
            sort_fields.add(new TSortField(TScene._Fields.CTIME.getFieldName(), -1));
        }
        for (TSortField sort : sort_fields)
        {
            orderBy.put(sort.getField(), sort.getAsc());
        }

        // create our pipeline operations, first with the $match
        logger.debug("match:{}", query);
        DBObject match = new BasicDBObject("$match", query);

        // the $group operation
        DBObject groupFields = new BasicDBObject("_id", "$" + TScene._Fields.COM_ID.getFieldName());
        groupFields.put("maxTime", new BasicDBObject("$max", "$" + sort_fields.get(0).getField()));
        DBObject group = new BasicDBObject("$group", groupFields);

        // the $sort operation
        DBObject sort = new BasicDBObject("$sort", new BasicDBObject("maxTime", -1));

        AggregationOutput output = mongo.aggregate(Tables.Scene, match, group, sort);
        int i = 0;
        for (DBObject com_db : output.results())
        {
            if (i >= req.getOffset() && i < (req.getOffset() + req.getLimit()))
            {
                int com_id = (Integer) com_db.get("_id");
                results.put(com_id, Lists.newArrayList());
                results_counts.put(com_id, 0);
            }
            i++;
        }

        //包含现场
        if (req.getWith_scene() > 0)
        {
            BasicDBObject origin_scene_query = new BasicDBObject();

            origin_scene_query.putAll(query.toMap());

            if (fields.contains(ListCompaniesRequest._Fields.SCENE_STATUS.getFieldName()))
            {
                if (req.getScene_statusSize() > 0)
                {
                    origin_scene_query.put(TScene._Fields.STATUS.getFieldName(), new BasicDBObject(QueryOperators.IN, req.getScene_status()));
                }
            }

            BasicDBObject scene_order = (BasicDBObject) orderBy.clone();

            for (Integer com_id : results.keySet())
            {
                BasicDBObject scene_query = (BasicDBObject) origin_scene_query.clone();
                scene_query.put(TScene._Fields.COM_ID.getFieldName(), com_id);

                if (req.fields.contains(ListCompaniesRequest._Fields.OWNER_GROUP.getFieldName())
                    && (req.getOwner_group().equals("y") || req.getOwner_group().equals("j")))
                {
                    scene_query.put(TScene._Fields.STAT.getFieldName()
                        + "."
                        + TSceneStat._Fields.OWNER_COUNTS.getFieldName()
                        + "."
                        + req.getOwner_group(), new BasicDBObject(QueryOperators.GT, req.getMin_owner_count()));
                }

                DBCursor scene_cur = mongo.cursor(Tables.Scene, scene_query, null, scene_order, 0, req.getScene_limit());
                List<TScene> scenes = results.get(com_id);
                results_counts.put(com_id, scene_cur.count());
                while (scene_cur.hasNext())
                {
                    BasicDBObject scene_db = (BasicDBObject) scene_cur.next();
                    if (null != scene_db)
                    {
                        scenes.add(ThriftUtil.dbObject2ThriftObject(scene_db, TScene.class));
                    }
                }
            }
        }

        if (fields.contains(ListCompaniesRequest._Fields.NO_SHOWS.getFieldName()) && req.getNo_showsSize() > 0)
        {
            List<String> no_show_list = req.getNo_shows();
            for (ListCompaniesResponse._Fields field : ListCompaniesResponse.metaDataMap.keySet())
            {
                if (no_show_list.contains(field.getFieldName()))
                {
                    Object no_show_obj = resp.getFieldValue(field);
                    if (no_show_obj instanceof List)
                    {
                        ((List<?>) no_show_obj).clear();
                    }
                    else if (no_show_obj instanceof Map)
                    {
                        resp.setFieldValue(field, null);
                    }
                }
            }
        }

        return resp;
    }

    @Override
    public ListCompanyTopDiaryResponse ListCompanyTopDiary(ListCompanyTopDiaryRequest req) throws TException
    {
        ListCompanyTopDiaryResponse resp = new ListCompanyTopDiaryResponse();
        resp.setTrace_tag(req.getTrace_tag());

        /* get scenes
         * 这个过程要获得到满足条件的对应现场 
         * */
        ListCompaniesRequest com_req = new ListCompaniesRequest();
        com_req.setFields(Arrays.asList(new String[] {
            ListCompaniesRequest._Fields.COM_IDS.getFieldName(),
            ListCompaniesRequest._Fields.SCENE_STATUS.getFieldName(),
            ListCompaniesRequest._Fields.WITH_SCENE.getFieldName(),
            ListCompaniesRequest._Fields.OWNER_GROUP.getFieldName() }));

        com_req.setCom_ids(req.getCom_ids());
        com_req.setWith_scene(1);
        com_req.setScene_limit(1);
        com_req.setScene_sort(Arrays.asList(new TSortField[] { new TSortField("stat.recent_time", -1) }));
        com_req.setScene_status(Arrays.asList(new Integer[] { 1 }));
        com_req.setOwner_group("y");
        com_req.setMin_owner_count(5);
        com_req.setSort(Arrays.asList(new TSortField("stat.recent_diary_time", -1), new TSortField("stat.recent_time", -1)));

        ListCompaniesResponse com_res = this.ListCompanies(com_req);

        /* get diaries
         * 这个过程从diary库中取对应的日记 
         * */
        if (!("succ".equals(com_res.getRes())))
        {
            resp.setRes(com_res.getRes());
            return resp;
        }
        Map<Integer, List<TScene>> company_scene_map = com_res.getResults();

        resp.setResults(com_res.getResults());

        for (Integer com_id : company_scene_map.keySet())
        {
            List<TScene> scenes = company_scene_map.get(com_id);
            for (TScene scene : scenes)
            {
                ListScenesRequest scene_req = new ListScenesRequest();
                scene_req.setTrace_tag(req.getTrace_tag());
                scene_req.setFields(Arrays.asList(new String[] {
                    ListScenesRequest._Fields._IDS.getFieldName(),
                    ListScenesRequest._Fields.WITH_DIARY.getFieldName(),
                    ListScenesRequest._Fields.DIARY_STATUS.getFieldName() }));
                scene_req.set_ids(Arrays.asList(new String[] { scene.get_id() }));
                scene_req.setLimit(1);

                scene_req.setWith_diary(1);
                scene_req.setDiary_status(Arrays.asList(new Integer[] { 1 }));
                scene_req.setDiary_limit(1);
                scene_req.setDiary_sort(Arrays.asList(new TSortField[] { new TSortField(TDiary._Fields.UTIME.getFieldName(), -1) }));
                ListScenesResponse scene_res = this.ListScenes(scene_req);

                List<TScene> scene_diary_result = scene_res.getResults();
                if (null != scene_diary_result && scene_diary_result.size() > 0)
                    company_scene_map.put(com_id, scene_res.getResults());
                break;
            }
        }
        return resp;
    }
}
